2011-07-08 73 views
2

例如,我有这样的机型:Django的 - 反向查找

class Person(models.Model): 
    name = models.CharField(max_length=20) 
    employer = models.CharField(max_length=20) 

class Car(models.Model): 
    person = models.ForeignKey(Person) 
    name = models.CharField(max_length=10) 
    model = models.CharField(max_length=10) 
    ... 

嗯,我想所有的人如何拥有一些特定的汽车:

people = Person.objects.filter(car__name="Toyota") 

现在我想写这个人与他的车的细节。我可以这样做:

for person in people: 
    ... 
    cars = person.car_set.filter(name="Toyota") 
    ... 

但它是数据库的下一个命中。我怎样才能避免这种情况?有什么办法可以简化吗?

回答

3

首先选择数据汽车及相关人当车名

cars = Car.objects.select_related("person").filter(name="Toyota").order_by("person") 

现在你把所有的名字是丰田汽车与人对于汽车,ordered_by人沿着汽车。

现在使用Python itertools.groupby到这组列表中为每个人

from itertools import groupby 
for k, g in groupby(cars, lambda x: x.person): 
     person = k 
     cars = list(g) 

现在在迭代你的人,他的车(他的名字是“丰田”)。 您会注意到只有单个查询发生,然后对缓存的信息执行以下操作。

+0

使用这种技术的一个副作用是,如果你有没有汽车的人,你不会在那个查询中看到。这是因为查询是通过汽车领域启动的。 – arustgi

0

退房select_related(),我用它之前,请务必跨越多个模型为一个大的查询很多小查询:https://docs.djangoproject.com/en/dev/ref/models/querysets/#select-related

它通过预填充QuerySet,所以你要car_set访问将已经那里并不会导致新的查询。

+0

即使我需要再次过滤吗?我不想要每辆车,但只有丰田。 – yetty

+0

如果你重复使用同一个'QuerySet',它可能会工作。查阅:https://docs.djangoproject.com/en/dev/topics/db/queries/#caching-and-querysets了解更多有关'QuerySet'缓存的细节。 – kcbanner

0

我不认为有避免额外的数据库命中。如果你担心拍过多的查询,你可以尝试这样的事:

from collections import defaultdict 

cars = Car.objects.filter(**kwargs).selected_related('person') 
owners = defaultdict(list) 

for car in cars: 
    owners[car.person].append(car) 

这应该只是一个查询选择所有相关的汽车以及他们相关的人