Django - Как объединить select_related и prefetch_related, чтобы уменьшить количество запросов - PullRequest
0 голосов
/ 25 мая 2020

Я хотел бы создать json, который представляет собой агрегирование данных из нескольких связанных вместе таблиц (отношения OneToOne и ManyToMany).

models.py

Team(models.Model):
    staff = models.OneToOneField(Staff, on_delete=models.CASCADE, related_name='team_staff')
    function = models.CharField()

Staff(models.Model):
    firstname = models.CharField()
    lastname = models.CharField()
    courses = models.ManyToManyField(Course, related_name='staff_course’)

Course(models.models):
    name = models.CharField()
    begin= models.DateField()
    end = models.DateField()

    def __str__(self):
        return '%s - %s' % (self.name, self.begin, self.end)

views.py

def all_team(request):
    team_query = Team.objects.all().select_related('staff')
    team_list = []
        for person in team_query:
            courses = Staff.objects.get(id=person.staff.id).courses.order_by('-begin')
            team_list.append({'fields': {'id': obj.id,
                                         'function': person.function,
                                         'firstname': person.staff.firstname,
                                         'lastname': person.staff.lastname,
                                         'last_course': str(courses.first()),
                                        }})
        json_data = json.dumps(team_list, cls=DjangoJSONEncoder)
        return HttpResponse(json_data, content_type='application/json')

Как видите, это не очень эффективно. Для каждого члена команды вы делаете запрос, чтобы получить последний пройденный курс. Можем ли мы добавить что-то вроде:

staff_query = Staff.objects.all().prefetch_related(Prefetch('courses', queryset=Course.objects.only('name').all()))

и объединить / объединить его с team_query .

Заранее благодарим вас за совет

1 Ответ

0 голосов
/ 05 июня 2020

Вы можете использовать обычный синтаксис соединения для предварительной выборки связанного поля из связанного поля:

team_query = (
  Team.objects.all()
  .select_related('staff')
  .prefetch_related(
    Prefetch('staff__courses').order_by('-begin'))
  )
)

Тогда получение последнего курса станет:

'last_course': str(person.staff.courses.first())
...