2011-07-08 5 views
2

Par exemple, j'ai ces modèles:Django - les recherches inverses

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

Eh bien, je veux obtenir tous les gens comment possèdent une voiture particulière:

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

Maintenant, je veux écrire ce peuple avec des détails sur sa voiture. Je peux le faire:

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

Mais c'est le prochain hit de la base de données. Comment puis-je éviter ça? Y at-il un moyen de le faire plus simple?

Répondre

3

Sélectionnez d'abord la voiture et la personne liée lorsque le nom de voiture

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

maintenant, vous avez toutes les voitures dont le nom est toyota avec la personne pour cette voiture, personne ordered_by.

Maintenant, utilisez le python itertools.groupby pour regrouper cette liste pour chaque personne

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

Maintenant dans cette itération vous avez la personne et ses voitures (dont le nom est « toyota »). Vous remarquerez qu'il n'y a qu'une seule requête qui se produit, puis les opérations suivantes s'exécutent sur les informations mises en cache.

+0

Un effet secondaire de l'utilisation de cette technique est que si vous avez une personne qui n'a pas de voiture, vous ne verrez pas cela dans cette requête. C'est parce que la requête est lancée sur le domaine des voitures. – arustgi

0

Découvrez select_related(), je l'ai utilisé avant de tourner de nombreuses petites requêtes qui couvrent plusieurs modèles dans une grande requête: https://docs.djangoproject.com/en/dev/ref/models/querysets/#select-related

Il fonctionne en prédéfinir la QuerySet, de sorte que votre accès à car_set sera déjà là et ne donnera pas une nouvelle requête.

+0

Même si je dois le filtrer à nouveau? Je ne veux pas toutes les voitures, mais seulement les Toyota. – yetty

+0

Si vous réutilisez le même 'QuerySet', cela peut fonctionner. Consultez: https://docs.djangoproject.com/en/dev/topics/db/queries/#caching-and-querysets pour plus de détails sur la mise en cache 'QuerySet'. – kcbanner

0

Je ne crois pas qu'il y ait de toute façon à éviter les hits de base de données supplémentaires. Si vous êtes préoccupé par faire trop de requêtes, vous pouvez essayer quelque chose comme ceci:

from collections import defaultdict 

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

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

Cela ne devrait être une requête qui sélectionne toutes les voitures concernées et les données relatives à leur personne liée

Questions connexes