Получить объекты внешнего ключа в одном запросе - Django

У меня есть 2 модели в моем коде django:

class ModelA(models.Model):
    name = models.CharField(max_length=255)
    description = models.CharField(max_length=255)
    created_by = models.ForeignKey(User)

class ModelB(models.Model):
    category = models.CharField(max_length=255)
    modela_link = models.ForeignKey(ModelA, 'modelb_link')
    functions = models.CharField(max_length=255)
    created_by = models.ForeignKey(User)

Say ModelA имеет 100 записей, все из которых могут иметь или не иметь ссылок на ModelB

Теперь скажите, что я хочу получить список каждой записи ModelA вместе с данными ModelB

Я бы сделал:

list_a = ModelA.objects.all()

Затем, чтобы получить данные для ModelB, мне пришлось бы делать

for i in list_a:
    i.additional_data = i.modelb_link.all()

Однако это запускает запрос для каждого экземпляра i. Таким образом, выполняется 101 запрос.

Есть ли способ запустить все это всего за один запрос. Или, по крайней мере, меньше, чем 101 запрос.

Я попытался вставить ModelA.objects.select_related().all(), но это не показало никакого эффекта.

Спасибо

Ответ 1

Как говорит Офри, select_related работает только с форвардными отношениями, а не с обратными.

Нет никакого встроенного способа автоматического отслеживания обратных отношений в Django, но см. мой пост в блоге для того, чтобы сделать это разумно эффективно. Основная идея состоит в том, чтобы сразу получить все связанные объекты для каждого элемента, а затем связать их вручную со своим связанным элементом - так что вы можете сделать это в 2 запросах, а не в n + 1.

Ответ 2

Django ORM - это хорошо, но некоторые вещи лучше делать вручную. Вы можете импортировать курсор соединения и выполнить необработанный sql в одном запросе.

from django.db import connection
cur=connection.cursor()
cur.execute(query)
rows = cur.fetchall()

ваш запрос должен выглядеть так (для MySQL)

SELECT * FROM appname_modela INNER JOIN appname_modelb ON appname_modela.id=appname_modelb.modela_link_id

Ответ 3

Причина .select_related() не работает, заключается в том, что .select_related() используется для использования внешних ключей. Модель ModelA не имеет внешнего ключа к ModelB. Его ModelB, который имеет внешний ключ для ModelA. (поэтому экземпляр ModelA может иметь несколько экземпляров ModelB, связанных с ним).

Вы можете использовать это, чтобы сделать это в 2 запросах и немного кода на Python:

list_b = ModelB.objects.all()
list_a = ModelA.objects.all()
for a in list_a:
    a.additional_data = [b for b in list_b if b.modela_link_id==a.id]