跳转至

tortoise-orm教程

简介

FastApi基于Starlette框架二次开发,使用pydantic做接口入参校验,可自动生成openapi形式的接口文档。

从Python3.6开始支持asyncio之后,许多支持此特性的python web框架开始陆续出现,如sanic、fastapi,利用异步特性+异步ORM,可以提高web服务的并发和吞吐量。

Django3.0之后才开始支持Asgi协议,至当前最新版本3.0.7依旧未支持异步ORM。

不严谨的场景下性能测试结果是 FastAPI>Flask>Django3。

tortoise-orm

tortoise-orm官网

tortoise-orm是一个类Django的异步支持的ORM,如果之前使用过Django的话,语法基本一致,无需特殊学习。

最大的区别就是所有的ORM语句执行前都需要加入await关键字,以此支持异步ORM读取操作。下面只简单介绍一些在使用过程中遇到的难点问题。

数据库连接

FastAPI的app主文件内, main.py:

from tortoise.contrib.fastapi import register_tortoise

register_tortoise(
   app,
   db_url=config.DATABASE_URI,
   modules={"models": ["client.models"]},
   generate_schemas=False,
   add_exception_handlers=False,
)

其中app为FastAPI的app对象。

modules为你models文件定义的路径。

generate_schemas为True的话会自动检测相关的表是否存在,不存在的话会自动尝试创建,表中的字段变化的话,不会自动更新的。

add_exception_handlers为True的话会自动为你处理ORM的DoesNotExist异常。

时区问题

create_time = fields.DatetimeField(auto_now_add=True, description='创建时间')
update_time = fields.DatetimeField(auto_now=True, description='更新时间')

models在定义的时候,经常会定义创建时间和更新时间,这个时间在不显式指定时,会由ORM自动补全,当前tortoise-orm(版本tortoise-orm==0.16.16)好像不支持指定时区,入库使用的时间为UTC时间,源码为:

\tortoise\fields\data.py Line 304:

def to_db_value(
   self, value: Optional[datetime.datetime], instance: "Union[Type[Model], Model]"
) -> Optional[datetime.datetime]:
   # Only do this if it is a Model instance, not class. Test for guaranteed instance var
   if hasattr(instance, "_saved_in_db") and (
       self.auto_now
       or (self.auto_now_add and getattr(instance, self.model_field_name) is None)
  ):
       value = datetime.datetime.utcnow()
       setattr(instance, self.model_field_name, value)
       return value
   return value

value强制指定为datetime.datetime.utcnow(),当前直接重写自定义的字段解决,将datetime.datetime.utcnow()修改为datetime.datetime.now()即可。

日志问题

tortoise-orm所有的操作日志都会log在一个名为db_client的log对象下,该log默认不会输出至控制台,若想查看每次操作对应的SQL语句,可以为该log对象添加handler即可。

# 数据库日志
db_log = logging.getLogger("db_client")
db_log.setLevel(10)
handler = WatchedFileHandler(filename='log/db_client.log')
formatter = logging.Formatter('%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s')
handler.setFormatter(formatter)
db_log.addHandler(handler)

想要查看单次ORM执行时对应的sql语句,直接在ORM语句后调用下.sql()方法即可。