Django агрегирование значений из связанной таблицы - PullRequest
0 голосов
/ 03 ноября 2018

Я использую Django для предоставления API-интерфейсов REST в своем приложении.

У меня есть таблица SaleInvoice и таблица SaleLineItems.

Я создал SerializerMethodField (sub_total) для вычисления значения на основе значений в полях таблицы SaleLineItems.

Когда я делаю запрос на публикацию / исправление в SaleLineItems, я могу получить sub_total в response.data.

Теперь, Мне нужен grand_total в таблице / сериализаторе SaleInvoice, который может объединять все значения sub_total из записей SaleLineItems, имеющих одинаковый идентификатор счета-фактуры.

Если я сделаю

SaleLineItems.objects.filter(sale_invoice=saleInvoiceId).aggregate(Sum('sub_total'))

Я получаю сообщение об ошибке, сообщающее, что я не могу использовать 'sub_total', но могу использовать другие обычные поля таблицы SaleLineItems.

Пожалуйста, помогите.

models.py

...

class SaleInvoice(models.Model):
    customer = models.ForeignKey(Customer, on_delete=models.CASCADE)
    serial_number = models.IntegerField(blank=True, null=True)
    amount_before_freight = models.FloatField(blank=True, null=True, default=0)
    freight = models.FloatField(blank=True, null=True, default=0)
    amount_after_freight = models.FloatField(blank=True, null=True, default=0)

    def __str__(self):
        return str(self.id)




class SaleLineItems(models.Model):
    sale_invoice = models.ForeignKey(SaleInvoice, on_delete=models.CASCADE)
    product_name = models.ForeignKey(Product, on_delete=models.PROTECT)
    product_qty = models.FloatField()
    product_rate = models.FloatField(blank=True, null=True, default=0)
    product_disc = models.FloatField(blank=True, null=True, default=0)


...

serializers.py

...

class SaleLineItemsSerializer(serializers.ModelSerializer):
    def get_sub_total(self, instance):
        return (
            Decimal(instance.product_qty)
            * Decimal(instance.product_rate)
            * Decimal(1-(instance.product_disc/100))
            ).quantize(Decimal("1.00"))

    sub_total = serializers.SerializerMethodField()

    class Meta:
        model = SaleLineItems
        fields = "__all__"

ОБНОВЛЕНИЕ ПОСЛЕ КОММЕНТАРИЙ:

class SaleInvoiceSerializer(serializers.ModelSerializer):
    def get_grand_total(self, saleInvoiceId):
        return SaleLineItems.objects.filter(sale_invoice=saleInvoiceId).aggregate(Sum('subTotal'))
    grand_total = serializers.SerializerMethodField()

    class Meta:
        model = SaleInvoice
        fields = "__all__"

Update2:

Удалено grand_total из serializers.py

Добавлено следующее свойство в models.py для модели SaleInvoice

Также добавлено related_name='items' для модели SaleLineItems

@property
def grandTotal(self):
    sum = 0
    for some in self.items.all():
        sum += some.sub_total
    return sum 

Это дает мне желаемый результат, но я понятия не имею, правильный ли это подход. Так что выложил это здесь.

1 Ответ

0 голосов
/ 03 ноября 2018

Обновите модель SaleLineItems до:

class SaleLineItems(models.Model):
    sale_invoice = models.ForeignKey(SaleInvoice, on_delete=models.CASCADE)
    product_name = models.ForeignKey(Product, on_delete=models.PROTECT)
    product_qty = models.FloatField()
    product_rate = models.FloatField(blank=True, null=True, default=0)
    product_disc = models.FloatField(blank=True, null=True, default=0)

    @property
    def sub_total(self):
        return (
            Decimal(self.product_qty)
            * Decimal(self.product_rate)
            * Decimal(1-(self.product_disc/100))
            ).quantize(Decimal("1.00"))

То, как вы его определили, не позволит Django узнать о вашем агрегированном свойстве, поскольку оно живет в сериализаторе, а модели Django ничего не знают о сериализаторах Django Rest Framework. Запрос используется на уровне Django ( Django ORM ), а не Django Rest Framework level.

Тогда ваш сериализатор может выглядеть так:

class SaleLineItemsSerializer(serializers.ModelSerializer):
    class Meta:
        model = SaleLineItems
        fields = ('id','sale_invoice','product_name','product_qty',
                  'product_rate', 'product_disc', 'sub_total',)

Обратите внимание на новое поле sub_total, указанное в сериализаторе.

В вашем views.py создайте набор:

class  SaleLineItemsList(generics.ListAPIView):
    queryset = SaleLineItems.objects.all()
    serializer_class = SaleLineItemsSerializer

В вашем urls.py добавить для Django 2.0 и более поздних версий добавить:

    path('salelineitems/', SaleLineItemsList.as_view()),

Для Django 1,7–1,11 добавить:

    url(r'^salelineitems/',SaleLineItemsList.as_view()),
...