Reputation: 499
I am using Django to serve REST APIs to my front end app.
I have a SaleInvoice table and SaleLineItems table.
I created a SerializerMethodField (sub_total) to calculate a value based on values in fields of SaleLineItems table.
When I make a post/patch request to SaleLineItems, I am able to get the sub_total in response.data.
Now, I need a grand_total in SaleInvoice table/serializer which can aggregate all sub_total values from SaleLineItems records which have the same Sale Invoice id.
If I do
SaleLineItems.objects.filter(sale_invoice=saleInvoiceId).aggregate(Sum('sub_total'))
I get an error telling me that I cannot use 'sub_total' but can use other regular fields of the SaleLineItems table.
Kindly help.
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__"
UPDATE AFTER COMMENTS:
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:
Removed grand_total
from serializers.py
Added the below property in models.py for SaleInvoice model
Also added related_name='items'
for SaleLineItems model
@property
def grandTotal(self):
sum = 0
for some in self.items.all():
sum += some.sub_total
return sum
This gets me the desired result but I have no clue if this is the right approach. So posted it here.
Upvotes: 0
Views: 90
Reputation: 3516
Update your SaleLineItems
model to be:
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"))
The way you defined it will not allow Django know about your aggregated property, as it lives in serializer and Django models know nothing about Django Rest Framework serializers. The query is used at Django level (Django ORM), not Django Rest Framework level.
Then your serializer can be like:
class SaleLineItemsSerializer(serializers.ModelSerializer):
class Meta:
model = SaleLineItems
fields = ('id','sale_invoice','product_name','product_qty',
'product_rate', 'product_disc', 'sub_total',)
Notice the new field sub_total
listed in the serializer.
In your views.py
create a viewset:
class SaleLineItemsList(generics.ListAPIView):
queryset = SaleLineItems.objects.all()
serializer_class = SaleLineItemsSerializer
In your urls.py add for Django 2.0 and later add:
path('salelineitems/', SaleLineItemsList.as_view()),
For Django 1.7- 1.11 add:
url(r'^salelineitems/',SaleLineItemsList.as_view()),
Upvotes: 1