【问题标题】:Joining ManyToMany fields with prefetch_related in Django在 Django 中使用 prefetch_related 加入 ManyToMany 字段
【发布时间】:2016-01-11 19:25:20
【问题描述】:

我可能遗漏了一些明显的东西,但我无法加入 ManyToMany 字段以在 Django 应用程序中工作。我有两个模型:

class Area(models.Model):
    name = CharField(...)

class Role(models.Model):
    name = CharField(...)
    areas = ManyToManyField('Area', ...)

我的目标是得到这个查询的等价物:

select a.name, r.name
from area a
join area_role ar on ar.area_id = a.id
join role r on ar.role_id = r.id
order by a.name, r.name

生成的数据集如下所示:

Area    Role
---------------------
A       My Role
A       Your Role
A       Yo Mamas Role
B       My Role
B       Some Other Role

正如您在示例中所见,我的角色 项出现了两次,每个区域一次。我知道我可以获取区域列表,然后获取每个角色的列表(导致 N+1 个查询),但如果可能的话,我希望能够高效处理。因此,我发现prefetch_related 可能是我想要使用的。但是,当我使用它时,我最终将所有区域值都设为 None。这是我尝试过的:

rqs = ( Role.objects.filter(areas__id__in=[1,2,3])
        .prefetch_related(areas).order_by('areas__name', 'name') )

for r in rqs:
    print("Area Name: " + str(r.areas.name))
    print("Role Name: " + str(r.name))

角色名称正确地出现,但地区名称没有。我在这里做错了什么?

【问题讨论】:

  • 不相关:为什么在areas = ManyToManyField('Area', ...) 的引号之间加上Area
  • print("Area Name: " + str(r.areas__name)) 你是说print("Area Name: " + str(r.areas.name))吗?
  • @Pynchia 在这种情况下他可以使用不带引号的区域,因为区域是在角色模型之前定义的。如果在角色之后定义了区域,则需要引号。 Full explanation here
  • @danielcorreia 是的,我的意思是r.areas.name。不知道那些双下划线是如何到达那里的......

标签: python django orm


【解决方案1】:

角色r 无法访问r.areas__name。您仍然需要通过r.areas.all() 访问这些角色。但是,通过使用 prefetch_related,您可以在一个额外的查询中获取所有相关对象,而不是 O(n) 次查询。

由于您想按区域名称排序,您可能应该为您的查询集使用Area 模型,然后遍历相关角色。

areas = Area.objects.filter(id__in=[1, 2, 3]).order_by('name').prefetch_related('role_set')

for area in areas:
    roles = area.role_set.all()
    for role in roles:
        print area.name, roles.name

只要Role 模型默认由name 订购,那应该会给您想要的排序。如果没有,您可以使用Prefetch 对象来订购相关的查询集。

【讨论】:

    猜你喜欢
    • 2018-09-25
    • 2021-07-24
    • 1970-01-01
    • 1970-01-01
    • 2016-11-28
    • 2012-02-04
    • 2014-02-04
    • 1970-01-01
    • 2013-01-24
    相关资源
    最近更新 更多