tortoise-orm多对多关系表的查询技巧
今天来分享tortoise-orm多对多关系表的查询技巧,利用好prefetch_related(预取)能大大提高我们的查询效率
补充:tortoise-orm是一款异步orm,它的使用方法和django-orm非常相似的,当我们学习了django的orm之后,我们上手tortoise-orm是非常轻松的,很多人都学习过django,当我们再学习FastAPI时使用tortoiseorm会大大降低我们的学习成本
场景
我们有两张表,且是多对多的关系,当我们查询其中一张表的数据的时候会碰到一个懒加载的问题:默认情况下我们只能获得被查询的数据,tortoise-orm不会给你返回相关联的数据(比如多对多关系)
你可能说那我拿到了数据再去查询对应的多对多数据不就完事了吗,当你查询一条数据的时候这样做也可以,但当一次查询大量数据的时候就大大浪费了资源
这么说可能有点抽象,我们用具体的例子来说明一下
释例
1.首先我们一张role表,一张permission表,两者时多对多的关系
from tortoise.models import Model
from tortoise import fields
class TimestampMixin(Model):
create_time = fields.DatetimeField(auto_now_add=True, description='创建时间')
update_time = fields.DatetimeField(auto_now=True, description="更新时间")
class Meta:
abstract = True
#Role
class Role(TimestampMixin):
role_name = fields.CharField(max_length=20,unique=True)
permission: fields.ManyToManyRelation["Permission"] = \
fields.ManyToManyField("models.Permission", related_name="role", on_delete=fields.CASCADE)
#Permission
class Permission(TimestampMixin):
scope = fields.CharField(max_length=30,unique=True)
role: fields.ManyToManyRelation[Role]
2.我们在数据库中创建一些数据
给role表创建5条数据
再给permission创建一条数据
再创建一条多对多关系
现在我们的数据库中有5个role一个permission,关系中id=1的role和id=1的pemission关联了起来
3.查询中遇到的问题和解决办法
我们用FastAPI写一个简单的接口,当我们查询所有role的信息的时候,我们发现并没有一同得到相关的permission信息
@router.get('all_role_permissions',summary='查询所有角色和其对应的权限')
async def getAllRolePermission():
roles = await Role.all().values()
retrun roles
#返回的结果,可以看到没有permission
[
{
"id": 1,
"create_time": "2024-02-08T09:55:53.329308+00:00",
"update_time": "2024-02-08T09:55:53.329308+00:00",
"role_name": "test_role"
},
{
"id": 2,
"create_time": "2024-02-14T03:25:32.624172+00:00",
"update_time": "2024-02-14T03:25:32.624172+00:00",
"role_name": "test_role2"
},
{
"id": 3,
"create_time": "2024-02-14T03:25:39.080627+00:00",
"update_time": "2024-02-14T03:25:39.080627+00:00",
"role_name": "test_role3"
},
{
"id": 4,
"create_time": "2024-02-14T03:25:43.066787+00:00",
"update_time": "2024-02-14T03:25:43.066787+00:00",
"role_name": "test_role4"
},
{
"id": 5,
"create_time": "2024-02-14T03:25:48.656371+00:00",
"update_time": "2024-02-14T03:25:48.656371+00:00",
"role_name": "test_role5"
}
]
我现在就想要permission咋办,你可能会这样写
@router.get('all_role_permissions',summary='查询所有角色和其对应的权限')
async def getAllRolePermission():
roles = await Role.all().values()
rep_data = []
for role in roles:
role_dict = dict(role)
role_permission = await role.permission.all().values()
role_dict['permission'] = role_permission
rep_data.append(role_dict)
print(rep_data)
retrun roles
上边的代码取出来所有的role,然后对得到的 [ ] 查询集进行遍历,对每一个queryset又跑到数据库里去取其对应的permission,最后封装在一起,我们为了得到6组数据跑去查询了7次,这就是所谓的N+1问题
可以看到这种做法是不好的效率也太低了吧!
因此引出我们的主角prefetch_related(预取),我们改进代码如下
@router.get('all_role_permissions',summary='查询所有角色和其对应的权限')
async def getAllRolePermission():
# roles = await Role.all().values()
# rep_data = []
# for role in roles:
# role_dict = dict(role)
# role_permission = await role.permission.all().values()
# role_dict['permission'] = role_permission
# rep_data.append(role_dict)
# print(rep_data)
roles = await Role.all().prefetch_related("permission")
rep_data = []
for role in roles:
role_dict = dict(role)
role_dict['permission'] = [dict(permission) for permission in role.permission]
rep_data.append(role_dict)
return rep_data
观察发现我们通过增加 .prefetch_related(“permission”)让tortoise-orm一次把全部数据都拿过来效率不久大大提高了,你可以看到我们全程只用了一次await,也就是只在数据库里拿了一次数据,之后直接通过role.permission直接就能在queryset里拿到permission的数据,高下立判。
来看看得到的结果吧,当然就是我们想要的
[
{
"create_time": "2024-02-08T09:55:53.329308+00:00",
"id": 1,
"role_name": "test_role",
"update_time": "2024-02-08T09:55:53.329308+00:00",
"permission": [
{
"create_time": "2024-02-08T09:54:25.300106+00:00",
"id": 1,
"scope": "use_test",
"update_time": "2024-02-08T09:54:25.300106+00:00"
}
]
},
{
"create_time": "2024-02-14T03:25:32.624172+00:00",
"id": 2,
"role_name": "test_role2",
"update_time": "2024-02-14T03:25:32.624172+00:00",
"permission": []
},
{
"create_time": "2024-02-14T03:25:39.080627+00:00",
"id": 3,
"role_name": "test_role3",
"update_time": "2024-02-14T03:25:39.080627+00:00",
"permission": []
},
{
"create_time": "2024-02-14T03:25:43.066787+00:00",
"id": 4,
"role_name": "test_role4",
"update_time": "2024-02-14T03:25:43.066787+00:00",
"permission": []
},
{
"create_time": "2024-02-14T03:25:48.656371+00:00",
"id": 5,
"role_name": "test_role5",
"update_time": "2024-02-14T03:25:48.656371+00:00",
"permission": []
}
]