В 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, поэтому, пожалуйста, укажите, если приведенный пример кода содержит какие-либо малоэффективные или нестандартные недостатки.
Большое спасибо всем за отличные предложения!