Помогите расширить запрос в БД (Django)

Есть задача, включить в запрос все заказы которые ссылаются на имя товара, сделал запрос на получение коллекции имен товаров с аннотированными полями, но "прикрутить" туда выше упомянутое не выходит, подскажите плз куда копать!

вот модели:

class Order(models.Model):
    number = models.IntegerField()
    create_date = models.DateTimeField()


class OrderItem(models.Model):
    order = models.ForeignKey(Order, on_delete=models.CASCADE, related_name='items')
    product_name = models.CharField(max_length=100)
    product_price = models.DecimalField(max_digits=20, decimal_places=0, null=True, blank=True)
    amount = models.IntegerField()

вот вьюха с запросом:

class ResultListView(ListView):
    model = Order
    success_url = reverse_lazy('top_results')
    template_name = 'order/top_results.html'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        orders_set_sorted = OrderItem.objects.all() \
            .annotate(name=Concat(Value('Товар - '), 'product_name',  output_field=CharField())) \
            .annotate(concat=Concat(Value('Заказ -  '), 'order__number',
                                    Value(',  Цена -  '), 'product_price',
                                    Value(',  Дата -  '), 'order__create_date',
                                    Value(';'), output_field=CharField()))\
            .order_by('-product_name').distinct('product_name')

        context['items'] = orders_set_sorted 
        return context

вот что выводит:

Товар-1; Заказ 1 - Цена 200 - Дата 01.01.2018 10:00;

вот то что я хочу получить:

Товар-1; Заказ 1 - Цена 200 - Дата 01.01.2018 10:00; Заказ N (который ссылается на Товар-1)- Цена 550 - Дата 10.01.2018 13:00; ...... + N заказов (которые ссылаются на Товар-1)


Ответы (2 шт):

Автор решения: cauf

Я бы на вашем месте не хранил данные о товаре в объекте позиции заказа. Это все таки разные вещи. Что вам нужно сделать (далее по тексту за точность кода не ручаюсь, так как на django давно ничего не писал; рассматривайте данный код как псевдо-код):

  1. Выделить отдельно класс товаров. Например как-то так:
class Product(models.Model):
    caption = models.CharField(max_length=100)
    price = models.DecimalField(max_digits=20, decimal_places=2, null=True, blank=True)
  1. В классе позиции заказа оставить только те значения, которые требуются для вычисления суммы, и ссылку на объект товара.
class OrderItem(models.Model):
    order = models.ForeignKey(Order, on_delete=models.CASCADE, related_name='items')
    product = models.ForeignKey(Product, on_delete=models.CASCADE, related_name='order_items')
    value = models.IntegerField()
    amount = models.DecimalField(max_digits=20, decimal_places=2, null=False, blank=False)
  1. Теперь вам не надо ничего придумывать для поиска всех заказов с конкретным товаром. Вы находите нужный товар, получаете все позиции заказов данного товара, а от них составляете список заказов.
def get_orders(product_caption):
    product = Product.objects.get(caption = product_caption)
    items = OrderItems.objects.filter(product = product)
    orders = []
    for it in items:
        if not it.order in orders:
            orders.append(it.order)
    return orders

Для более глубоко понимания вопросов декомпозиции, советую почитать книг по философии ООП и основам реляционной теории.

→ Ссылка
Автор решения: scand

Всетаки смог реализовать не создавая дополнительных таблиц и полей в базе :)

def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        orders_set_sorted = OrderItem.objects.all() \
            .annotate(name=Concat(Value('Товар - '), 'product_name',  output_field=CharField())) \
            .annotate(concat=Concat(Value('Заказ -  '), 'order__number',
                                    Value(',  Цена -  '), 'product_price',
                                    Value(',  Дата -  '), 'order__create_date',
                                    output_field=CharField()))\
            .order_by('-product_name')
​
​
      
​
        queryset = orders_set_sorted.values('name').order_by('name')\
            .annotate(
            concat_=StringAgg('concat', delimiter='; '),
            customer_name=F('name')
        )
        
​
        context['items'] = queryset
        return context
→ Ссылка