在Django中使用prefetch_related连接ManyToMany字段

时间:2015-10-13 21:26:27

标签: python django orm

我可能会遗漏一些显而易见的东西,但是我无法在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可能是我想要使用的。但是,当我使用它时,我最终得到的所有Area值为。这是我试过的:

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))

角色名称正确用于骑行,但区域名称没有。我在这里做错了什么?

1 个答案:

答案 0 :(得分:7)

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

由于您希望按地区名称排序,因此您应该使用prefetch_related模型作为查询集,然后循环执行相关角色。

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排序,那么这应该会为您提供所需的顺序。如果没有,您可以使用Prefetch对象来订购相关的查询集。