Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
523 views
in Technique[技术] by (71.8m points)

foreign keys - Django: getting the list of related records for a list of objects

I have two models related one-to many:

class Person(models.Model):
    name      = models.CharField(max_length=255);
    surname   = models.CharField(max_length=255);     
    age       = models.IntegerField(); 


class Dog(models.Model):
    name      = models.CharField(max_length=255);
    owner     = models.ForeignKey('Person');

I want to output a list of persons and, below each person a list of dogs he has. Here's how I can do it:

in view:

persons = Person.objects.all()[0:100];

in template:

{% for p in persons %}
    {{ p.name }} has dogs:<br />
    {% for d in persons.dog_set.all %}
        - {{ d.name }}<br />
    {% endfor %}
{% endfor %}

But if I do it like that, Django will execute 101 SQL queries which is very inefficient.

I tried to make a custom manager, which will get all the persons, then all the dogs and links them in python, but then I can't use paginator (my another question: Django: Paginator + raw SQL query ) and it looks quite ugly.

Is there a more graceful way doing this?

UPDATE

Solution based on blog entry by @Daniel Roseman

all_objects = Person.objects.select_related().all();

paginator = Paginator(all_objects, 100);

try:
    page = int(request.GET.get('page', '1'))
except ValueError:
    page = 1

try:
    current_page = paginator.page(page)
except (EmptyPage, InvalidPage):
    current_page = paginator.page(paginator.num_pages)

person_list = dict([(obj.id, obj) for obj in current_page.object_list])
id_list = [obj.id for obj in current_page.object_list]

objects = Dog.objects.filter(owner__id__in=id_list)

relation_dict = {}
for obj in objects:
    relation_dict.setdefault(obj.owner_id, []).append(obj)

for id, related_items in relation_dict.items():
    person_list[id].dogs = related_items
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

With the newer versions of Django (>=1.7 or maybe even some older ones) you can just replace your

persons = Person.objects.all()[0:100]

with

persons = Person.objects.prefetch_related('dog_set').all()[0:100]

and that's it, no need to keep using the old workaround if you are on Django>=1.7

the official docs on prefetch_related are here: https://docs.djangoproject.com/en/2.0/ref/models/querysets/#prefetch-related


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...