Skip to main content

简单的 python 异步 ORM,构建时考虑到关系

项目描述

https://img.shields.io/pypi/v/tortoise-orm.svg?style=flat https://pepy.tech/badge/tortoise-orm/month https://github.com/tortoise/tortoise-orm/workflows/gh-pages/badge.svg https://github.com/tortoise/tortoise-orm/workflows/ci/badge.svg https://coveralls.io/repos/github/tortoise/tortoise-orm/badge.svg https://app.codacy.com/project/badge/Grade/844030d0cb8240d6af92c71bfac764ff

介绍

Tortoise ORM 是一个易于使用的asyncio ORM (对象关系映射器),受 Django 启发。

Tortoise ORM 在构建时考虑到了关系以及对优秀和流行的 Django ORM 的钦佩。它的设计中铭刻着您不仅使用表格,还使用关系数据。

您可以在文档中找到文档

对于 SQLite、MySQL 和 PostgreSQL 以及 Microsoft SQL Server 和 Oracle,CPython >= 3.7 支持 Tortoise ORM。

为什么要构建 Tortoise ORM?

Python 有许多现有的和成熟的 ORM,不幸的是,它们的设计与如何处理 I/O 的范式相反。 asyncio是一种相对较新的技术,具有非常不同的并发模型,最大的变化在于 I/O 的处理方式。

然而,Tortoise ORM 并不是构建异步ORM 的第一次尝试。尽管有很多开发人员试图将同步 Python ORM 映射到异步世界,但最初的尝试并没有一个干净的 API。

因此我们开始了 Tortoise ORM。

Tortoise ORM 被设计为功能性且熟悉的,以简化希望切换到asyncio的开发人员的迁移。

与其他 Python ORM 相比,它也表现良好。在我们的基准测试中,我们测量不同的读取和写入操作(行/秒,越多越好),这是与 Pony ORM 的交易场所:

https://raw.githubusercontent.com/tortoise/tortoise-orm/develop/docs/ORM_Perf.png

ORM 有什么用?

当您构建使用关系数据库的应用程序或服务时,有时您无法仅使用参数化查询甚至查询构建器。您只是不断重复自己,为每个实体编写略有不同的代码。代码不知道数据之间的关系,因此您最终几乎是手动连接数据。访问数据库的方式也很容易出错,这可能会被 SQL 注入攻击利用。您的数据规则也是分布式的,增加了管理数据的复杂性,更糟糕的是,可能导致这些规则的应用不一致。

ORM(对象关系映射器)旨在解决这些问题,通过集中您的数据模型和数据规则,确保您的数据得到安全管理(提供对 SQL 注入的免疫力)并跟踪关系,因此您不必.

入门

安装

首先你必须像这样安装 Tortoise ORM:

pip install tortoise-orm

您还可以使用您的数据库驱动程序安装(aiosqlite是内置的):

pip install tortoise-orm[asyncpg]

对于MySQL

pip install tortoise-orm[asyncmy]

对于Microsoft SQL Server / Oracle未完全测试):

pip install tortoise-orm[asyncodbc]

快速教程

乌龟的主要实体是tortoise.models.Model。你可以开始编写这样的模型:

from tortoise.models import Model
from tortoise import fields

class Tournament(Model):
    id = fields.IntField(pk=True)
    name = fields.TextField()

    def __str__(self):
        return self.name


class Event(Model):
    id = fields.IntField(pk=True)
    name = fields.TextField()
    tournament = fields.ForeignKeyField('models.Tournament', related_name='events')
    participants = fields.ManyToManyField('models.Team', related_name='events', through='event_team')

    def __str__(self):
        return self.name


class Team(Model):
    id = fields.IntField(pk=True)
    name = fields.TextField()

    def __str__(self):
        return self.name

定义完所有模型后,tortoise 需要您初始化它们,以便在模型之间创建反向关系并将您的数据库客户端与适当的模型匹配。

你可以这样做:

from tortoise import Tortoise

async def init():
    # Here we connect to a SQLite DB file.
    # also specify the app name of "models"
    # which contain models from "app.models"
    await Tortoise.init(
        db_url='sqlite://db.sqlite3',
        modules={'models': ['app.models']}
    )
    # Generate the schema
    await Tortoise.generate_schemas()

在这里,我们在名为db.sqlite3的本地目录中创建一个到 SQLite 数据库的连接。然后我们发现并初始化模型。

Tortoise ORM 目前支持以下数据库:

  • SQLite(需要aiosqlite

  • PostgreSQL(需要asyncpg

  • MySQL(需要asyncmy

  • Microsoft SQL Server / Oracle(需要asyncodbc

generate_schema在空数据库上生成模式。Tortoise 默认在安全模式下生成模式,其中包括IF NOT EXISTS子句,因此您可以将其包含在主代码中。

之后,您可以开始使用您的模型:

# Create instance by save
tournament = Tournament(name='New Tournament')
await tournament.save()

# Or by .create()
await Event.create(name='Without participants', tournament=tournament)
event = await Event.create(name='Test', tournament=tournament)
participants = []
for i in range(2):
    team = await Team.create(name='Team {}'.format(i + 1))
    participants.append(team)

# M2M Relationship management is quite straightforward
# (also look for methods .remove(...) and .clear())
await event.participants.add(*participants)

# You can query a related entity with async for
async for team in event.participants:
    pass

# After making a related query you can iterate with regular for,
# which can be extremely convenient when using it with other packages,
# for example some kind of serializers with nested support
for team in event.participants:
    pass


# Or you can make a preemptive call to fetch related objects
selected_events = await Event.filter(
    participants=participants[0].id
).prefetch_related('participants', 'tournament')

# Tortoise supports variable depth of prefetching related entities
# This will fetch all events for Team and in those events tournaments will be prefetched
await Team.all().prefetch_related('events__tournament')

# You can filter and order by related models too
await Tournament.filter(
    events__name__in=['Test', 'Prod']
).order_by('-events__participants__name').distinct()

移民

Tortoise ORM 使用Aerich作为其数据库迁移工具,在其文档中查看更多详细信息。

贡献

请查看贡献指南

执照

该项目在 Apache 许可证下获得许可 - 请参阅LICENSE.txt文件了解详细信息。

项目详情