Как prefetch_related поля для связанного поля в Django

У меня есть несколько примеров того, как поля prefetch_related в прямой и обратной связи в Django, но у меня есть сомнения относительно того, как это можно применить, если мы хотим предварительно выбрать все поля связанной модели.

Например, если я хочу получить весь контент из следующих моделей, используя HealthCheck в качестве отправной точки. Какой запрос будет наиболее оптимизирован для достижения этой цели?

class HealthCheck(models.Model):
    id = models.Integer()
    person = models.ForeignKey('Person')


class Person(models.Model):
    profile = models.ForeignKey('Profile')
    vaccines = models.ManyToManyField('vaccines', through='PersonVaccines')


class Profile(models.Model):
    name = models.CharField(max_length=16)


class PersonVaccines(models.Model):
    person = models.ForeignKey(Person)
    vaccine = models.ForeignKey('Vaccine')


class Vaccine(models.Model):
    name = models.CharField(max_length=16)

Я пробовал что-то вроде этого, но, похоже, не работает:

from django.db.models import Prefetch

HealthCheck.objects.filter(id=1).prefetch_related(
    Prefetch(
        'person__vaccines',
        queryset=PersonVaccines.objects.select_related('person', 'person__profile', 'vaccine')
    )
)

Как я могу предварительно загрузить все связанное содержимое?


person Ander    schedule 27.05.2021    source источник


Ответы (1)


Это должно быть возможно с:

HealthCheck.objects.filter(id=1).select_related('person__profile').prefetch_related('person__vaccines')

select_related будет получать все связанные поля до profile.

Затем prefetch_related следует выполнить предварительную выборку всех связанных объектов до тех пор, пока vaccines

EDIT: протестировано и подтверждено, что генерируются только 2 запроса, когда я создал 3 объекта вакцины для одного человека, проверяющего здоровье (n - моя функция для подсчета выполненных запросов):

In [2]: health_check = HealthCheck.objects.filter(id=3).select_related('person__profile').prefetch_related('person__vaccines').get()
     ...:

In [3]: n()
2

In [4]: for v in health_check.person.vaccines.all():
     ...:     print(v.name)
     ...:
v1
v2
v3

In [5]: n()
0

 
person bdbd    schedule 28.05.2021