tortoise-orm关系表怎么查询?用好prefetch_related(预取)大大提高效率

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": []
  }
]
  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值