Django ORM объединяет отношения "многие ко многим" в одном запросе

Если у нас есть 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.

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
4
0
4 020
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Чтобы получить прямой ответ, перейдите к пункту 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 

Спасибо, обновили вопрос, добавив некоторые пояснения к вашим вопросам.

David Veza 20.11.2018 10:07

Я знаю, что фильтрация через xhr может быть решением, но это все еще не дает ответа на вопрос.

David Veza 20.11.2018 10:40

@DavidVeza, я заменил образец на образец N: M.

dani herrera 20.11.2018 11:22

@DavidVeza, тебе нравится тарантул папы Дайса?

dani herrera 20.11.2018 12:02

отличный! извините за поздний ответ ... занялся другими делами :)

David Veza 20.11.2018 16:50

Другие вопросы по теме