select_related with reverse foreign key

时间:2014-04-16 23:14:45

标签: django django-models django-queryset django-1.5 django-managers

我在Django中有两个模型。第一个是工作职能(职位)报告到哪些其他职位的层次结构,第二个是人员和他们所担任的工作职能。

class PositionHierarchy(model.Model):
    pcn = models.CharField(max_length=50)
    title = models.CharField(max_length=100)
    level = models.CharField(max_length=25)
    report_to = models.ForeignKey('PositionHierachy', null=True)


class Person(model.Model):
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)
    ...
    position = models.ForeignKey(PositionHierarchy)

当我有人事记录并且我想找到该人的经理时,我必须这样做

manager = person.position.report_to.person_set.all()[0]
# Can't use .first() because we haven't upgraded to 1.6 yet

如果我找到了QuerySet的人,我可以使用Person.objects.select_related('position', 'position__reports_to').filter(...)加入(并避免第二次访问数据库)position和report_to,但有没有办法避免另一个前往数据库获取person_set?我尝试将'position__reports_to__person_set'position__reports_to__person添加到select_related,但这似乎不会更改查询。这是prefetch_related的用途吗?

我想创建一个自定义管理器,这样当我进行查询以获取Person记录时,我也会获得他们的PositionHeirarchy和他们的经理的Person记录,而不会再往返数据库。这就是我到目前为止所做的:

class PersonWithManagerManager(models.Manager):
    def get_query_set(self):
        qs = super(PersonWithManagerManager, self).get_query_set()
        return qs.select_related(
            'position',
            'position__reports_to',
        ).prefetch_related(
        )

1 个答案:

答案 0 :(得分:30)

是的,这就是prefetch_related()的用途。它需要一个额外的查询,但我们的想法是它会立即获得所有相关信息,而不是每Person一次。

在你的情况下:

qs.select_related('position__report_to')
  .prefetch_related('position__report_to__person_set')

应该需要两个查询,而不管原始查询集中Persons的数量。

比较documentation

中的这个例子
>>> Restaurant.objects.select_related('best_pizza')
                      .prefetch_related('best_pizza__toppings')