Reputation: 2466
For example, I have these models:
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)
...
I want to get all people who own some specific car:
people = Person.objects.filter(car__name="Toyota")
Now I want to write these people out with details about their own car. I can do this:
for person in people:
...
cars = person.car_set.filter(name="Toyota")
...
But it hit the database again. How can I avoid this? Is there any way to do this simpler?
Upvotes: 2
Views: 2733
Reputation: 848
First select the car and the related person when the car name
cars = Car.objects.select_related("person").filter(name="Toyota").order_by("person")
Now you have all the cars whose name is toyota along with the person for that car, ordered_by person.
Now use the python itertools.groupby to group this list for each person
from itertools import groupby
for k, g in groupby(cars, lambda x: x.person):
person = k
cars = list(g)
Now in that iteration you have the person and his cars (whose name is "toyota"). You'll notice that there is only single query that occurs, and then the following operations execute on the cached information.
Upvotes: 3
Reputation: 19082
I don't believe there is anyway to avoid the additional database hits. If you're concerned about making too many queries, you can try something like this:
from collections import defaultdict
cars = Car.objects.filter(**kwargs).selected_related('person')
owners = defaultdict(list)
for car in cars:
owners[car.person].append(car)
This should only be one query that selects all the relevant cars and the data about their related person
Upvotes: 0
Reputation: 4078
Check out select_related()
, I've used it before to turn many small queries that span multiple models into one large query: https://docs.djangoproject.com/en/dev/ref/models/querysets/#select-related
It works by pre-populating the QuerySet
, so your access to car_set
will already be there and won't result in a new query.
Upvotes: 0