Django как сделать эквивалент ВНЕШНЕГО СОЕДИНЕНИЯ СЛЕВА (показывать левые данные, даже если не существует правой пары)?

#django #django-views #django-orm

#django #django-просмотры #django-orm

Вопрос:

У меня есть две таблицы:

 class Product(models.Model):
    code = models.ForeignKey(Product)
    regularprice = models.DecimalField (max_digits=8, decimal_places=2)

class Override(models.Model):
    productcode = models.ForeignKey(Product)
    specialprice = models.DecimalField (max_digits=8, decimal_places=2)


Data Example
============
Product
Code    RegularPrice
C101        1.25
C102        2.50
C103        3.00

Override
ProductCode SpecialPrice
C102        1.50
  

Я хочу сделать эквивалент левого внешнего соединения. Результирующий набор, которого я хочу достичь, будет:

 Code    RegularPrice     SpecialPrice
C101        1.25        NULL
C102        2.50          1.50
C103        3.00         NULL
  

Как бы я это сделал?


РЕДАКТИРОВАТЬ: я пытаюсь получить базовый прайс-лист с необязательными значениями переопределения. Генерируются базовые цены, к которым добавляются любые значения переопределения (или NULL, если их нет).

Очень жаль, но я пропустил важный элемент этого вопроса, думая, что это упростит его. У каждого переопределения есть столбец с именем Customer . Класс переопределения имеет УНИКАЛЬНЫЕ ЗНАЧЕНИЯ для «customerid» и «code» (так что одно переопределение для каждого кода для каждого клиента).

Я хочу создать прайс-лист всех продуктов , если для конкретного клиента присутствуют какие-либо переопределения, они будут показаны вместе с соответствующей линейкой продуктов.

Запрос ORM выглядит следующим образом: pricelist = Product.objects.select_related().filter(переопределить__customerid=1)

Но это выполняет только обычное соединение (исключая любой продукт, который не связан с переопределением), я хочу, чтобы ЛЕВОЕ ВНЕШНЕЕ СОЕДИНЕНИЕ отображало все в таблице продуктов, с присоединением любых переопределений (если они присутствуют или NULL)

Большое спасибо за любую помощь, которую вы можете предложить!

Ответ №1:

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

 products = Product.objects.all()
  

или любой Product другой запрос, который вы хотите, затем выполните цикл в шаблоне, например:

 {% for product in products %}
    {{product.code}}
    {{product.regularprice}}
    {{product.override.specialprice|default:"NULL"}}
{% endfor %}
  

Если вам нужен более эффективный запрос к базе данных, вы можете использовать select_related (docs):

 products = Product.objects.select_related('override')
  

но это будет работать, только если вы используете OneToOneField for productcode , а не a ForeignKey :

 class Override(models.Model):
    productcode = models.OneToOneField(Product)
  

Комментарии:

1. select_related не может следовать обратным связям. Вы вроде бы заметили, что … но то, что вы показываете, просто не будет работать

2.@John — согласно документам, select_related может следовать обратным связям, но только для OneToOneField s, как я отмечаю в своем ответе (я подчеркну это немного больше, если это поможет). Похоже, что использование a OneToOneField может быть вполне разумным вариантом в случае OP.

3. В целом следует избегать OneToOneFields. Они представляют собой ненужное соединение для данных, которые вы, скорее всего, должны иметь только в основной модели. Единственное реальное хорошее применение для них — это если у вас есть какое-то подмножество данных, которое действительно дорого извлечь из базы данных и редко используется. С учетом сказанного, мое чтение моделей подразумевает, что у него один ко многим, и он может этого не осознавать

4. Большое спасибо за ответ, я вижу, что пропустил важную информацию и пересмотрел свой вопрос.

Ответ №2:

В зависимости от того, что вы настроили, связь между Product и Override является одним из многих. Это означает, что для каждого продукта у вас будет (потенциально) много переопределений.

Итак, теперь вам нужно подумать о том, что вы хотите сгенерировать, и это определит, как вы хотите запросить это.

Если вам нужен список переопределений, просто запросите Override.objects.all().select_related()

Затем вы можете сгенерировать список переопределений и связанных с ними данных продукта, следуя foreignkeys:

 o = Override.objects.all()[0]
o.product.regularprice
  

Если вам нужен список продуктов, каждый из которых имеет свой собственный список переопределений, вам следует посмотреть на функцию itertools grouby (или, если вы находитесь в шаблоне, используйте regroup тег django)

Комментарии:

1. Большое спасибо за ответ, но я пропустил важную информацию, я пересмотрел свой вопрос.

Ответ №3:

Я считаю, что в настоящее время это невозможно с ORM Django, и вам нужно использовать raw запрос.