Джанго: получение списка связанных записей для списка объектов - PullRequest
5 голосов
/ 28 марта 2010

У меня есть две модели, связанные один-ко-многим:

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');

Я хочу вывести список людей и ниже каждого человека список собак, которые у него есть. Вот как я могу это сделать:

в поле зрения:

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

в шаблоне:

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

Но если я сделаю это так, Django выполнит 101 SQL-запрос, что очень неэффективно.

Я пытался создать собственный менеджер, который будет получать всех людей, затем всех собак и связывать их в python, но тогда я не могу использовать paginator (мой другой вопрос: Django: Paginator + raw SQL запрос ) и выглядит довольно некрасиво.

Есть ли более изящный способ сделать это?

UPDATE

Решение на основе записи в блоге @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

1 Ответ

1 голос
/ 23 апреля 2018

С более новыми версиями Django (> = 1.7 или, может быть, даже некоторыми старыми) вы можете просто заменить

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

с

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

и все, больше не нужно использовать старый обходной путь, если вы используете Django> = 1.7

официальные документы по prefetch_related находятся здесь: https://docs.djangoproject.com/en/2.0/ref/models/querysets/#prefetch-related

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...