菜鸟笔记
提升您的技术认知

fastapi之tortoise-ag真人游戏

fastapi是一个很优秀的框架,但是缺少一个合适的orm,官方代码里面使用的是sqlalchemy,异步也是使用的这个。但是我这边看到有tortoise-orm这个异步orm框架,不知道效率如何,这里先学习,之后做一个性能测试比较一下。
整个框架非常接近django,如果我没写的地方,要么是和django差不多,要么是没这功能。

fastapi引入

在main.py文件里面引入如下代码:

from fastapi import fastapi, httpexception
from pydantic import basemodel
from models import user_pydantic, userin_pydantic, users
from tortoise.contrib.fastapi import httpnotfounderror, register_tortoise
app = fastapi(title="tortoise orm fastapi example")
...
register_tortoise(#这里是启动app的,之后会考虑和使用uvicorn启动的性能差别
    app,
    db_url="sqlite://:memory:",#数据库信息
    modules={
  "models": ["models"]},#models列表
    generate_schemas=true,#如果数据库为空,则自动生成对应表单,生产环境不要开
    add_exception_handlers=true,#生产环境不要开,会泄露调试信息
)

引入tortoise-orm的方法:register_tortoise

def register_tortoise(
    app: fastapi,
    config: optional[dict] = none,
    config_file: optional[str] = none,
    db_url: optional[str] = none,
    modules: optional[dict[str, list[str]]] = none,
    generate_schemas: bool = false,
    add_exception_handlers: bool = false,
) -> none:
    """
    在fastapi注册startup和shutdown
    使用 ``config``, ``config_file``或 ``(db_url, modules)``三者之一来配置
    示例
    ----------
    config:
        dict containing config:
        example
        -------
            {
                'connections': {
                    # dict format for connection
                    'default': {
                        'engine': 'tortoise.backends.asyncpg',
                        'credentials': {
                            'host': 'localhost',
                            'port': '5432',
                            'user': 'tortoise',
                            'password': 'qwerty123',
                            'database': 'test',
                        }
                    },
                    # using a db_url string
                    'default': 'postgres://postgres:qwerty123@localhost:5432/events'
                },
                'apps': {
                    'models': {
                        'models': ['__main__'],
                        # if no default_connection specified, defaults to 'default'
                        'default_connection': 'default',
                    }
                }
            }
    config_file:
        path to .json or .yml (if pyyaml installed) file containing config with
        same format as above.
    db_url:
        use a db_url string. see :ref:`db_url`
    modules:
        dictionary of ``key``: [``list_of_modules``] that defined "apps" and modules that
        should be discovered for models.
    generate_schemas:
        true立即生成模式。仅适用于开发环境或sqlite ' ':memory: ' '数据库,生产环境数据库一定手动生成。
    add_exception_handlers:
        为' ' doesnotexist ' ' & ' ' integrityerror ' '添加一些自动异常处理程序。
不建议用于生产系统,因为它可能会泄漏数据。
    """
    pass

创建对应数据模型

tortoise-orm能使用的数据类型还挺丰富,满足了日常使用的需求。

创建model

from tortoise import fields
from tortoise.models import model
class user(model):
	id=fields.intfield(pk=true)#主键必不可少

通过继承的方式创建model

from tortoise import fields
from tortoise.models import model
class timestampmixin():
    created_at = fields.datetimefield(null=true, auto_now_add=true)
    modified_at = fields.datetimefield(null=true, auto_now=true)
class namemixin():
    name = fields.charfield(40, unique=true)
class myabstractbasemodel(model):
    id = fields.intfield(pk=true)
    class meta:
        abstract = true
#注意数据库里面对应表顺序可能会比较乱。。
class usermodel(timestampmixin, myabstractbasemodel):
    # 覆盖继承的字段
    id = fields.uuidfield(pk=true)
    # 新增一些字段
    first_name = fields.charfield(20, null=true)
    class meta:
        table = "user"
class rolemodel(timestampmixin, namemixin, myabstractbasemodel):
    class meta:
        table = "role"

设置数据库字段field

主要有如下字段(这些字段都没啥好说的,):

#常规字段
bigintfield,
binaryfield,
booleanfield,
charenumfield,
charfield,
datefield,
datetimefield,
decimalfield,
floatfield,
intenumfield,#继承于smallintfield
intfield,
jsonfield,
smallintfield,
textfield,
timedeltafield,
uuidfield,
#关系字段(relation不知道啥用,)
backwardfkrelation,
backwardonetoonerelation,
foreignkeyfield,#外键
foreignkeynullablerelation,
foreignkeyrelation,
manytomanyfield,#多对多
manytomanyrelation,#反向代码提示的工具
onetoonefield,#一对一
onetoonenullablerelation,
onetoonerelation,
reverserelation,

关系键讲解:
foreignkeyfield:

tournament = fields.foreignkeyfield('models.tournament', related_name='events')#related_name关键字参数,用于为引用的模型定义查询自己的字段,默认为名字 set
participants = fields.manytomanyfield('models.team', related_name='events')
modified = fields.datetimefield(auto_now=true)
prize = fields.decimalfield(max_digits=10, decimal_places=2, null=true)

反向代码提示
在使用外键的时候,我们需要获取到反向代码提示(即被绑定的model查询绑定model),这个时候需要使用relation字段来提示,(其实你不加也没啥关系,只是个提示作用,在django里面是编辑器和插件提前内置了相关字段所以不用手写
示例代码如下:

from tortoise.models import model
from tortoise import fields
class tournament(model):
    id = fields.intfield(pk=true)
    name = fields.charfield(max_length=255)
    events: fields.reverserelation["event"]#仅用于代码提示,注意events必须和event里面的外键指定的related_name同名
class event(model):
    id = fields.intfield(pk=true)
    name = fields.charfield(max_length=255)
    tournament: fields.foreignkeyrelation[tournament] = fields.foreignkeyfield(
        "models.tournament", related_name="events"
    )
    participants: fields.manytomanyrelation["team"] = fields.manytomanyfield(
        "models.team", related_name="events", through="event_team"
    )#注意多对多,两个model里面都要写,虽然复杂了点,但是有代码提示还是很合算的。。through在django里面是指定多对多表的名字和功能,需要手动创建,这里可能是示例代码不全吧。。得测试
class team(model):
    id = fields.intfield(pk=true)
    name = fields.charfield(max_length=255)
    events: fields.manytomanyrelation[event]#反向关系,
字段介绍

基本字段:
source_field :自定义数据库对应字段名称
generated :是否映射到数据库,
pk 是否为主键
null
default 可以为值或可调用对象
unique
index
description 描述功能,数据库注释用

自定义字段

tortoise-orm支持自定义字段,这个功能挺好用的,这里先不讲了,一般用不到。。。请自行查看:link.
可以通过继承,然后重构相关字段控制字段在数据库的存储方式

设置meta

from tortoise import fields
from tortoise.models import model
class user(model):
	id=fields.intfield(pk=true)#主键必不可少
	class meta:
		abstract=true#抽象模型,用于继承
		table="xxx"#该模型对应的表名称
		unique_together=(("field_a", "field_b"), )#设置唯一索引,参考django
		table_description = ""#数据库对该表的注释
		indexes=(("field_a", "field_b"), )#指定列集为非唯一索引,类似django在字段上的index
		ordering = ["name", "-score"]#设置默认查询结果的顺序

model模型方法

#常用查询方法我就不提了,讲一些不常用的
annotate()#使用额外的函数/聚合对结果进行再过滤,,
bulk_create()#批量插入:
user.bulk_create([
    user(name="...", email="..."),
    user(name="...", email="...")
])
check()#检查model数据是否正确
describe()#序列化model,返回json
exists()#true/false 记录是否存在筛选器参数模式的model
register_listener()#侦听器,参数为信号,注意,更新到最新版才有。。数据的保存和删除前后可以被监听,挺有用的一个东西,使用说明查看链接:https://tortoise-orm.readthedocs.io/en/latest/examples/basic.html#model-signals
update_from_dict()#通过dict更新数据,配合schema很有用,主要用于字段更新,schema.dict有一个只获取填写结果字段的方法,配合这个可以实现局部更新

查询

参考django,略。

q对象查询

这一块主要针对复杂查询。
有时,您需要执行比简单 and 提供的更复杂的查询。幸运的是,我们有q对象来调味的东西,并帮助您找到你需要的。然后,这些 q 对象可用作参数。.filter().filter()

q 对象用途极多,例如用例:
创建 or 筛选器
嵌套筛选器
倒置过滤器
例如,查找名称或 的事件:event 1event 2

found_events = await event.filter(
    q(name='event 1') | q(name='event 2')
)
#等效于:
	found_events = await event.filter(
    q(q(name='event 1'), q(name='event 2'), join_type="or")#如果省略join_type,则为and
)

字段过滤

在搜索的时候增加后缀可以实现字段的过滤效果:
比如:

teams = await team.filter(name__icontains='con')

相关参数如下:

  • not
  • in- 检查字段的值是否位于传递列表中
  • not_in
  • gte- 大于或等于传递的值
  • gt- 大于传递的值
  • lte- 低于或等于传递的值
  • lt- 低于传递值
  • range- 介于和给定两个值之间
  • isnull- 字段为空
  • not_isnull- 字段不为空
  • contains- 字段包含指定的子字符串
  • icontains- 不区分大小写contains
  • startswith- 如果字段以值开头
  • istartswith- 不区分大小写startswith
  • endswith- 如果字段以值结尾
  • iendswith- 不区分大小写endswith
  • iequals- 区分大小写等于

预取

通过预取,可以减少数据库读取次数,然后提高响应速度
有时只需要获取某些相关记录。您可以使用对象实现:prefetch
示例:

tournament_with_filtered = await tournament.all().prefetch_related(
    prefetch('events', queryset=event.filter(name='first'))
).first()

更多的参考:预取详情

f表达式

某些时候,我们只是需要将数据进行一次计算或处理然后保存,我们并不在意值是多少,只是想把值进行我们指定的修改,就可以使用f表达式,这样就可以减少一次数据库读取(我感觉好像没卵用啊。。。)
参考如下:

from tortoise.expressions import f
await user.filter(id=1).update(balance = f('balance') - 10)
await user.filter(id=1).update(balance = f('balance')   f('award'), award = 0)
# or use .save()
user = await user.get(id=1)
user.balance = f('balance') - 10
await user.save(update_fields=['balance'])

功能和聚合


请参考功能和聚合

事务

略 请参考事务

根据model生成schema

讲道理schema这个东西名字挺奇葩的。。。不过既然ag真人试玩娱乐官网这么弄就这么弄吧。这个可以很方便的生成相关字段
注意,schema不要有相同的类名,会报错的

user_pydantic = pydantic_model_creator(users, name="user")
userin_pydantic = pydantic_model_creator(users, name="userin", exclude_readonly=true)

下面这个文档里面还没讲解;
可以通过在model里面创建一个class pydanticmeta来实现创建schema的控制:

class pydanticmeta:
    """
    the ``pydanticmeta`` class is used to configure metadata for generating the pydantic model.
    usage:
    .. code-block:: python3
        class foo(model):
            ...
            class pydanticmeta:
                exclude = ("foo", "baa")
                computed = ("count_peanuts", )
    """
    #: if not empty, only fields this property contains will be in the pydantic model
    include: tuple[str, ...] = ()
    #: fields listed in this property will be excluded from pydantic model
    exclude: tuple[str, ...] = ()
    #: computed fields can be listed here to use in pydantic model
    computed: tuple[str, ...] = ()
    #: use backward relations without annotations - not recommended, it can be huge data
    #: without control
    backward_relations: bool = true
    #: maximum recursion level allowed
    max_recursion: int = 3
    #: allow cycles in recursion - this can result in huge data - be careful!
    #: please use this with ``exclude``/``include`` and sane ``max_recursion``
    allow_cycles: bool = false
    #: if we should exclude raw fields (the ones have _id suffixes) of relations
    exclude_raw_fields: bool = true
    #: sort fields alphabetically.
    #: if not set (or ``false``) then leave fields in declaration order
    sort_alphabetically: bool = false

如果你想跨表搜索或join搜索,在computed里面定义。

网站地图