概述
fastapi是一个很优秀的框架,但是缺少一个合适的orm,官方代码里面使用的是sqlalchemy,异步也是使用的这个。但是我这边看到有tortoise-orm这个异步orm框架,不知道效率如何,这里先学习,之后做一个性能测试比较一下。 整个框架非常接近django,如果我没写的地方,要么是和django差不多,要么是没这功能。
fastapi引入
Copy 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 解释
Copy 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
Copy from tortoise import fields
from tortoise . models import Model
class User ( Model ):
id = fields . IntField (pk = True ) #主键必不可少
通过继承的方式创建Model
Copy 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
Copy
#常规字段
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 :
Copy 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里面是编辑器和插件提前内置了相关字段所以不用手写) 示例代码如下:
Copy 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 ] #反向关系,
字段介绍
Copy source_field :自定义数据库对应字段名称
Copy description 描述功能,数据库注释用
自定义字段
tortoise-orm支持自定义字段,这个功能挺好用的,这里先不讲了,一般用不到。。。请自行查看:link. 可以通过继承,然后重构相关字段控制字段在数据库的存储方式
Copy # 设置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模型方法
#常用查询方法我就不提了,讲一些不常用的
Copy annotate () #使用额外的函数/聚合对结果进行再过滤,,
bulk_create () #批量插入:
User . bulk_create ([
User (name = "..." , email = "..." ),
User (name = "..." , email = "..." )
])
check () #检查model数据是否正确
describe () #序列化model,返回json
exists () #True/False 记录是否存在筛选器参数模式的model
register_listener () #侦听器,参数为信号,注意,更新到最新版才有。。数据的保存和删除前后可以被监听,挺有用的一个东西,
update_from_dict () #通过dict更新数据,配合schema很有用,主要用于字段更新,schema.dict有一个只获取填写结果字段的方法,配合这个可以实现局部更新
查询
参考django,略。
Q对象查询
这一块主要针对复杂查询。 有时,您需要执行比简单 AND 提供的更复杂的查询。幸运的是,我们有Q对象来调味的东西,并帮助您找到你需要的。然后,这些 Q 对象可用作参数。.filter().filter()
Q 对象用途极多,例如用例:
倒置过滤器 例如,查找名称或 的事件:Event 1Event 2
Copy 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
)
字段过滤
在搜索的时候增加后缀可以实现字段的过滤效果: 比如:
Copy teams = await Team . filter (name__icontains = 'CON' )
相关参数如下:
icontains- 不区分大小写contains
istartswith- 不区分大小写startswith
iendswith- 不区分大小写endswith
预取
通过预取,可以减少数据库读取次数,然后提高响应速度 有时只需要获取某些相关记录。您可以使用对象实现:Prefetch 示例:
Copy tournament_with_filtered = await Tournament . all (). prefetch_related (
Prefetch ( 'events' , queryset = Event. filter (name = 'First' ))
). first ()
F表达式
某些时候,我们只是需要将数据进行一次计算或处理然后保存,我们并不在意值是多少,只是想把值进行我们指定的修改,就可以使用F表达式,这样就可以减少一次数据库读取(我感觉好像没卵用啊。。。) 参考如下:
Copy 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()
Copy user = await User . get (id = 1 )
user . balance = F ( 'balance' ) - 10
await user . save (update_fields = [ 'balance' ])
功能和聚合
略 请参考功能和聚合
事务
略 请参考事务
根据Model生成Schema
讲道理schema这个东西名字挺奇葩的。。。不过既然官网这么弄就这么弄吧。这个可以很方便的生成相关字段
注意,schema不要有相同的类名,会报错的
Copy User_Pydantic = pydantic_model_creator (Users, name = "User" )
UserIn_Pydantic = pydantic_model_creator (Users, name = "UserIn" , exclude_readonly = True )
下面这个文档里面还没讲解; 可以通过在model里面创建一个class PydanticMeta来实现创建schema的控制:
Copy 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里面定义。