В Django, как сделать запрос join с условиями соответствия в дополнение к внешнему ключу?

У нас есть написанный вручную SQL-запрос для доказательства концепции, и мы надеемся реализовать функцию с помощью фреймворка Django.

В частности, QuerySet в Django обычно реализует запрос join путем сопоставления внешнего ключа с первичным ключом ссылающейся таблицы. Однако в приведенном ниже примере SQL нам нужны дополнительные условия сопоставления помимо внешнего ключа, как eav_phone.attribute_id = 122 в приведенном ниже фрагменте примера.

...
  left outer join eav_value as eav_postalcode
    on t.id = eav_postalcode.entity_id and eav_phone.attribute_id = 122
...

Вопрос:

Нам интересно, есть ли способ сделать это с помощью фреймворка Django или библиотек.

Основания и технические детали:

Сценарий представляет собой отчет, состоящий из транзакций с настроенными столбцами Django-EAV. Эта библиотека реализует таблицу eav_value, состоящую из столбцов различных типов данных, например value_text, value_date, value_float и т.д. Мы используем форк Django-EAV, обновленный до Python 2, а не Django-EAV2, но новая версия следует тому же дизайну схемы базы данных, насколько мы знаем.

Итак, приложение определяет продукт с атрибутами в определенных типах данных, и мы назвали это метаданными в этом вопросе, например:

attribute_id slug datatype
122 postalcode text
123 phone text
... ... e.g. date, float, etc. ...

Одна транзакция - это одна сущность, а несколько записей с совпадающими entity_id соответствуют различным настраиваемым атрибутам. И мы хотим построить динамический QuerySet в соответствии с метаданными, чтобы собрать настроенные столбцы с left outer join аналогично приведенному ниже образцу SQL запроса.

select 
    t.id, t.create_ts
  , eav_postalcode.value_text as postalcode
  , eav_phone.value_text as phone
from
  (
    select * transactions
    where product_id = __PRODUCT_ID__
  ) as t
  left outer join eav_value as eav_postalcode
    on t.id = eav_postalcode.entity_id and eav_phone.attribute_id = 122
  left outer join eav_value as eav_phone
    on t.id = eav_phone.entity_id and eav_phone.attribute_id = 123
;

Мы последовали подсказке @NickODell на FilteredRelation, и наше предварительное решение выглядит следующим образом:

transaction_eav = transaction.annotate(
  eav_postalcode=FilteredRelation('eav_values', condition=Q(eav_values__attribute_id=22))
)

transaction_eav = transaction_eav.annotate(
    value_postalcode=F('eav_postalcode__value_text)}
)

Мы новички в Django ORM, поэтому, пожалуйста, укажите, если приведенный пример кода содержит какие-либо малоэффективные или нестандартные недостатки.

Большое спасибо всем за отличные предложения!

Вернуться на верх