Как отобразить только одну запись из дочерней модели для каждого элемента заголовка в Django ListView с использованием different () - PullRequest
1 голос
/ 22 февраля 2020

Я использую две связанные модели в своем приложении Django. Объекты, созданные в моделях, отображаются с использованием класса listview . В дочерней модели я могу создать несколько строк на основе определенной контрольной даты. Когда я пытаюсь отобразить значения из обеих моделей, отображаются все дочерние объекты для соответствующих полей FK (где есть несколько записей).

Позвольте мне прояснить ситуацию, как показано ниже:

models.py

class MatPriceDoc(models.Model):
    mat_price_doc_number = models.IntegerField(null=True, blank=True.....)
    mat_num = models.ForeignKey(Material, on_delete=.......)
    mat_group = models.ForeignKey(MatGrp, on_delete=.......)
    doc_type = models.CharField(max_length=2, null=True, default='RF'....)
    create_date = models.DateField(default=timezone.now,...)

class MatPriceItems(models.Model):
    price_doc_header = models.ForeignKey(MatPriceDoc, on_delete=...)
    price_item_num = models.CharField(max_length=3, default=10,...)
    price_valid_from_date = models.DateField(null=True, verbose_name='From date')
    price_valid_to_date = models.DateField(null=True, verbose_name='To date')
    mat_price = models.DecimalField(max_digits=7, decimal_places=2, null=True, blank=True,...)

views.py

class MatPriceListView(ListView):
    template_name = "mat_price_list.html"
    context_object_name = 'matprice'
    model = MatPriceDoc

    def get_context_data(self, **kwargs):
        context = super(MatPriceListView, self).get_context_data(**kwargs)
        context.update({
            'mat_price_item_list': MatPriceItems.objects.distinct().order_by('price_doc_header'),  #This is where I have tried **distinct()**
        })
        return context

    def get_queryset(self):
        return MatPriceDoc.objects.order_by('mat_num')

Теперь цена материала периодически изменяется и всегда будет более одной позиции цены для каждого материала на каждую отчетную дату (price_valid_from_date). Моя цель - показать последнюю цену всех тех материалов, для которых существуют ценовые документы. Мое затруднение заключается в том, как подобрать только одну из множества ценовых позиций для одной и той же комбинации материала и документа. Моя строка кода 'mat_price_item_list': MatPriceItems.objects.distinct().order_by('price_doc_header'),, конечно, не дает никакого результата (все элементы цены отображаются в последовательных столбцах).

Можно ли показать только один элемент цены в просмотре списка?

Редактировать

На следующем рисунке показаны цены, поддерживаемые на различные даты для материалов. То, что я пытался получить, это только цена за последнюю (действительную) дату, отображаемую для конкретного материала. Таким образом, в данном случае (как показано) для каждого элемента должны отображаться цены только за 4 марта 2020 года MS и HSD .

enter image description here

Редактировать 2

Так выглядят данные дочерней модели для экземпляра заголовка с номером c (здесь номер 32 ) (изображение получить из дочерней таблицы с помощью браузера данных):

enter image description here

Столбцы: Child obj. Арт. № / Цена / Действ. с / Действ. до / Hdr Do c №. / Child Obj Row №.

Мое мышление было : не могу ли я взять только первый объект (подмножество) из коллекции до c номеров? В данном случае (см. Изображение) выполните c нет. 32 имеет три дочерних элемента (номер 148 , 149 и 156 ). Разве нельзя только взять номер предмета 156 и выбросить остальные?

Я пытался:

MatPriceItems.objects.order_by('-price_valid_to_date').first()

, но поднимает ошибка "MatPriceItems" object is not iterable.

Что позволило бы мне получить единственный элемент для элемента заголовка и отобразить его?

Ответы [ 2 ]

0 голосов
/ 22 февраля 2020

Я также добавляю то, что, по моему мнению, является правильным ответом, основанным на повторном чтении вашего вопроса.

Мне кажется, что запрос необходимо изменить на:

MatPriceItems.objects.all().select_related(
    'mat_price_docs'
).order_by(
    'price_doc_header__mat_num'
)

т.е.

class MatPriceListView(ListView):
    template_name = "mat_price_list.html"
    context_object_name = 'matprice'
    model = MatPriceDoc

    def get_context_data(self, **kwargs):
        context = super(MatPriceListView, self).get_context_data(**kwargs)
        context.update({
            'mat_price_item_list': MatPriceItems.objects.all().select_related('mat_price_docs').order_by('price_doc_header__mat_num'),
        })
        return context

    def get_queryset(self):
        return MatPriceDoc.objects.order_by('mat_num')

Для этого вы также добавляете аргумент related_name в поле price_doc_header ForeignKey.

class MatPriceItems(models.Model):
    price_doc_header = models.ForeignKey(MatPriceDoc, related_name='mat_price_docs', on_delete=...)
    price_item_num = models.CharField(max_length=3, default=10,...)
    price_valid_from_date = models.DateField(null=True, verbose_name='From date')
    price_valid_to_date = models.DateField(null=True, verbose_name='To date')
    mat_price = models.DecimalField(max_digits=7, decimal_places=2, null=True, blank=True,...)

Как всегда, любые изменения в ваши модели - пожалуйста, повторите команды управления makemigarations и migrate.

Редактировать : Вы можете даже определить новый набор запросов, используя аннотацию для ссылки на ForeignKey :

def get_queryset(self):
        return MatPriceItems.objects.all().order_by(
                    'price_doc_header__mat_num'
               ).annotate(
                    mat_price_doc_number = F('price_doc_header__mat_price_doc_number')
                    ...
               )

NB Вы можете импортировать выражение F () как from django.db.models import F

0 голосов
/ 22 февраля 2020

Полагаю, вам нужно следующее:

class MatPriceListView(ListView):
    template_name = "mat_price_list.html"
    context_object_name = 'matprice'
    model = MatPriceDoc

    def get_context_data(self, **kwargs):
        context = super(MatPriceListView, self).get_context_data(**kwargs)
        context.update({
            'mat_price_item_list': MatPriceItems.objects.all().('-price_doc_header').first(),
        })
        return context

    def get_queryset(self):
        return MatPriceDoc.objects.order_by('mat_num')

Изменение ключевой линии:

MatPriceItems.objects.all().order_by('-price_doc_header').first()

Так, что вы заказываете по убыванию price_doc_header (отсюда минус перед это значение). Затем возьмите .first() в возвращенном объекте <QuerySet>.

Аналогично, будет также допустимо следующее:

MatPriceItems.objects.all().order_by('price_doc_header').last()

Вы также можете использовать встроенный Django в агрегации в ORM:

from django.db.models import Max

MatPriceItems.objects.all().aggregate(Max('price_doc_header'))

Сложив все это вместе, я бы сказал, что лучшим решением для читабельности кода будет использование встроенной в Django функции Max:

from django.db.models import Max

class MatPriceListView(ListView):
    template_name = "mat_price_list.html"
    context_object_name = 'matprice'
    model = MatPriceDoc

    def get_context_data(self, **kwargs):
        context = super(MatPriceListView, self).get_context_data(**kwargs)
        context.update({
            'mat_price_item_list': MatPriceItems.objects.all().aggregate(Max('price_doc_header'))
        })
        return context

    def get_queryset(self):
        return MatPriceDoc.objects.order_by('mat_num')

Потому что он точно говорит любому коллеге, что вы делаете: принимаете максимальное значение.

Обновление : на второй взгляд ваш вопрос - я думаю, вы тоже можете нужно сделать что-то вроде .filter() ... затем сделать .annotate() для Max ...

...