Если у нас есть 2 модели A, B с отношением «многие ко многим».
Я хочу получить запрос sql, подобный этому:
SELECT *
FROM a LEFT JOIN ab_relation
ON ab_relation.a_id = a.id
JOIN b ON ab_relation.b_id = b.id;
Итак, в django, когда я пытаюсь:
A.objects.prefetch_related('bees')
Я получаю 2 запроса, похожие на:
SELECT * FROM a;
SELECT ab_relation.a_id AS prefetch_related_val_a_id, b.*
FROM b JOIN ab_relation ON b.id = ab_relation.b_id
WHERE ab_relation.a_id IN (123, 456... list of all a.id);
Учитывая, что A и B имеют умеренно большие таблицы, я считаю, что django делает это слишком медленно для моих нужд.
Возникает вопрос: можно ли получить вручную написанный запрос левого соединения через ORM?
Правки, чтобы ответить на некоторые пояснения:
Да, LEFT OUTER JOIN был бы предпочтительнее, чтобы получить все A в наборе запросов, а не только те, которые связаны с B (обновленный sql).
Умеренно большой означает ~ 4k строк каждая, а слишком медленный означает ~ 3 секунды (при первой загрузке, до кеширования Redis). Имейте в виду, что на странице есть другие запросы.
На самом деле да, нам нужен только B.one_field, но, попробовав Prefetch('bees', queryset=B.objects.values('one_field')), мы получили сообщение об ошибке: вы не можете использовать values в предварительной выборке.
Набор запросов будет использоваться в качестве параметров для поля формы с множественным выбором, где нам нужно будет представить объекты A, которые связаны с B, с дополнительной строкой из поля B.





Чтобы получить прямой ответ, перейдите к пункту 6)
Давайте поговорим шаг за шагом.
1) N: M выбрать. Вы говорите, что хотите получить такой запрос:
SELECT *
FROM a JOIN ab_relation ON ab_relation.a_id = a.id
JOIN b ON ab_relation.b_id = b.id;
Но это не настоящий запрос N: M, потому что вы получаете только объекты, относящиеся к A-B. Запрос должен использовать outer joins. Хотя бы как:
SELECT *
FROM a left outer JOIN
ab_relation ON ab_relation.a_id = a.id left outer JOIN
b ON ab_relation.b_id = b.id;
В других случаях вы получаете только модели A с соответствующим B.
2) Читать большие таблицы Вы говорите «умеренно большие столы». Тогда вы уверены, что хотите прочитать всю таблицу из базы данных? В веб-среде не принято читать большое количество данных, и в этом случае вы можете разбивать данные на страницы. Может быть, это не веб-приложение? Зачем нужно читать эти большие таблицы? Нам нужен контекст, чтобы ответить на ваш вопрос. Вы уверены, что вам нужны все поля из обеих таблиц?
3) Выбрать из Вы уверены, что вам нужны все поля из обеих таблиц? Может быть, если вы читаете только некоторые значения, этот запрос будет выполняться быстрее.
A.objects.values( "some_a_field", "anoter_a_field", "Bs__some_b_field" )
4) Как резюме. ORM - мощный инструмент, две операции чтения выполняются «быстро». Я пишу несколько идей, но, возможно, нам нужно больше контекста, чтобы ответить на ваш вопрос. Что означает умеренно большие таблицы, пшеница означает медленные, что вы делаете с этими данными, сколько полей или байтов содержит каждая строка из каждой таблицы, ....
Отредактированоd Потому что OP отредактировал вопрос.
5) Используйте правильные элементы управления пользовательского интерфейса. Ты говоришь:
The queryset will be used as options for a multi-select form-field, where we will need to represent A objects that have a relation with B with an extra string from the B.field.
Это похоже на анти-шаблон для отправки клиенту 4k строк для формы. Я предлагаю вам перейти к живому контролю, который загружает только необходимые данные. Например, фильтрация по тексту. Взгляните на потрясающий проект django-select2.
6) Вы говорите
The question is: Is it possible to obtain the left join manually written query through the ORM?
Ответ: да, вы можете сделать это с помощью values, как я сказал в пункте 3. Пример: Material и ResultatAprenentatge являются отношением N: M:
>>> print( Material
.objects
.values( "titol", "resultats_aprenentatge__codi" )
.query )
Запрос:
SELECT "material_material"."titol",
"ufs_resultataprenentatge"."codi"
FROM "material_material"
LEFT OUTER JOIN "material_material_resultats_aprenentatge"
ON ( "material_material"."id" =
"material_material_resultats_aprenentatge"."material_id" )
LEFT OUTER JOIN "ufs_resultataprenentatge"
ON (
"material_material_resultats_aprenentatge"."resultataprenentatge_id" =
"ufs_resultataprenentatge"."id" )
ORDER BY "material_material"."data_edicio" DESC
Я знаю, что фильтрация через xhr может быть решением, но это все еще не дает ответа на вопрос.
@DavidVeza, я заменил образец на образец N: M.
@DavidVeza, тебе нравится тарантул папы Дайса?
отличный! извините за поздний ответ ... занялся другими делами :)
Спасибо, обновили вопрос, добавив некоторые пояснения к вашим вопросам.