Select_related с обратными внешними ключами
У меня две модели в Django. Первая имеет иерархию того, какие функции работы (позиции) сообщают, на какие другие позиции, а вторая - на людей и на какую функцию работы они держат.
class PositionHierarchy(model.Model):
pcn = models.CharField(max_length=50)
title = models.CharField(max_length=100)
level = models.CharField(max_length=25)
report_to = models.ForeignKey('PositionHierachy', null=True)
class Person(model.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
...
position = models.ForeignKey(PositionHierarchy)
Когда у меня есть запись Person, и я хочу найти менеджера, я должен сделать
manager = person.position.report_to.person_set.all()[0]
# Can't use .first() because we haven't upgraded to 1.6 yet
Если я получаю людей с QuerySet
, я могу присоединиться (и избежать второй поездки в базу данных) с позицией и report_to с помощью Person.objects.select_related('position', 'position__reports_to').filter(...)
, но есть ли способ избежать другой поездки в базу данных получить person_set? Я попробовал добавить 'position__reports_to__person_set'
или просто position__reports_to__person
к select_related
, но это не похоже на изменение запроса. Это то, что prefetch_related
для?
Я хотел бы создать пользовательский менеджер, чтобы, когда я делаю запрос для получения записей Person, я также получаю их PositionHeirarchy и их запись Person менеджера без дополнительных поездок в базу данных. Это то, что у меня есть до сих пор:
class PersonWithManagerManager(models.Manager):
def get_query_set(self):
qs = super(PersonWithManagerManager, self).get_query_set()
return qs.select_related(
'position',
'position__reports_to',
).prefetch_related(
)
Ответы
Ответ 1
Да, для этого служит prefetch_related()
. Для этого потребуется дополнительный запрос, но идея состоит в том, что он получит всю соответствующую информацию сразу, а не один раз за Person
.
В вашем случае:
qs.select_related('position__report_to')
.prefetch_related('position__report_to__person_set')
должен требовать два запроса, независимо от количества Persons
в исходном наборе запросов.
Сравните этот пример с документацией:
>>> Restaurant.objects.select_related('best_pizza')
.prefetch_related('best_pizza__toppings')