Django Aggregation: Summation of Multiplication of two fields with ForienKey - django

I have two model as below
class Product(models.Model):
title = models.CharField(max_length=128)
slug = models.SlugField()
price = models.DecimalField(default=0.0,decimal_places=2,max_digits=15,validators=[MinValueValidator(Decimal('0.00'))])
def __str__(self):
return str(self.title)
class OrderItem(models.Model):
product = models.ForeignKey(Product,on_delete=models.CASCADE)
quantity = models.IntegerField(default=1)
def __str__(self):
return f"{self.quantity} of {self.product.title}"
def subtotal(self):
return self.quantity * self.product.price
I want the total of all subtotal using aggregate but
total1 = OrderItem.objects.all().aggregate(total=Sum(F('product.price') * F('quantity')))['total']
returns Cannot resolve keyword 'product.price' into field. Choices are: id, order, product, product_id, quantity and
total2 = OrderItem.objects.all().aggregate(total=Sum(F('subtotal') * F('quantity')))['total']
returns Cannot resolve keyword 'subtotal' into field. Choices are: id, order, product, product_id, quantity, error

After some research I found that
from django.db.models import Sum, F
total = OrderItem.objects.all().aggregate(total=Sum(F('product__price') * F('quantity')))['total']
will do the work

Related

Get value from django field and change it dynamically

I have created two models with two fields which are quantity and quantity_given, so I want to change the value of quantity field by adding the value of quantity + quantity given. For example
if quantity = 4 and quantity_given = 8
therefore the new value of quantity field will be 12.
Here are the source code for my models
class Stock(models.Model):
`name = models.CharField(max_length=30)`
def __str__(self):
return self.name
class Medicine(models.Model):
stock = models.ForeignKey(Stock, on_delete=models.CASCADE)
name = models.CharField(max_length=30)
quantity = models.IntegerField()
def __str__(self):
return self.name
class MedicineGiven(models.Model):
medicine = models.ForeignKey(Medicine, on_delete=models.CASCADE)
quantity_given = models.IntegerField()
You can have a method in MedicineGiven, like:
class MedicineGiven(models.Model):
medicine = models.ForeignKey(Medicine, on_delete=models.CASCADE)
quantity_given = models.IntegerField()
#property
def quantity(self):
return self.quantity_given + int(self.medicine.quantity)
In your views, you can get quantity of MedicineGiven like:
medicine_given = MedicineGiven.objects.get(pk=id) # Just a example code
medicine_given.quantity
EDIT
If you want to save the quantity in database, then you can override save() method:
class MedicineGiven(models.Model):
medicine = models.ForeignKey(Medicine, on_delete=models.CASCADE)
quantity_given = models.IntegerField()
def save(self, *args, **kwargs):
quantity = self.quantity_given + int(self.medicine.quantity)
self.medicine.quantity = quantity
self.medicine.save()
super().save(*args, **kwargs)

Sum and store values in another table

I am new to Django. My query is
I store some quantity of components in a table. I want to sum the quantity sorting it by components and then use the summed quantity in some other table to do further calculations. How can I achieve this..
#views.py
class PurchaseCreateView(CreateView):
model = Purchase
fields = '__all__'
success_url = reverse_lazy("purchase_form")
def get_context_data(self,**kwargs):
context = super().get_context_data(**kwargs)
context['purchases'] = Purchase.objects.all()
return context
#models.py
class Purchase(models.Model):
purchase_date = models.DateField()
invoice_no = models.CharField(max_length=200,unique=True)
supplier =models.ForeignKey(Supplier,on_delete=models.CASCADE)
components = models.ForeignKey(Components,on_delete=models.CASCADE)
quantity = models.PositiveIntegerField()
remarks = models.TextField(max_length=500,blank=True,null=True)
def __str__(self):
return str(self.pk)
# Below is the Model where I want to use the summed quantity for further calc
class IntermediateStage(models.Model):
producttype = models.ForeignKey(ProductType,on_delete=models.CASCADE)
components = models.ForeignKey(Components,on_delete=models.CASCADE)
processingstage = models.ForeignKey(ProcessingStage,on_delete=models.CASCADE)
unitsrequired = models.PositiveIntegerField()
quantity = models.PositiveIntegerField(blank = True)**#want to store summed qty here**
total_quantity = models.IntegerField(blank= True)
class Meta:
unique_together = [['producttype','components','processingstage']]
def __str__(self):
return str(self.id)
#Components Model
class Components(models.Model):
name = models.CharField(max_length=100,unique=True)
producttype = models.ManyToManyField(ProductType, through='IntermediateStage')
def __str__(self):
return self.name

Django, how to get total sum of methods with agregate sum

how to get total sum of methods in django using agregate ? I used this for fields and it work fine but for method it doesn t return anything.
class Invoice(models.Model):
date = models.DateField(default=timezone.now)
client = models.ForeignKey('Client',on_delete=models.PROTECT)
def total(self):
total = self.invoiceitem_set.aggregate(sum=Sum('subtotal'))
return round(total[("sum")] or 0, 2)
class InvoiceItem(models.Model):
invoice = models.ForeignKey('Invoice', on_delete=models.CASCADE)
product = models.ForeignKey(Product, on_delete=models.PROTECT)
price = models.DecimalField(max_digits=20, decimal_places=2)
quantity = models.DecimalField(max_digits=20, decimal_places=2)
def subtotal(self):
return self.price * self.quantity
You can not reference a method, since the database does not know anything about subtotal. You thus should work with F objects [Django-doc] to multiply the price with the quantity:
from django.db.models import F
class Invoice(models.Model):
date = models.DateField(default=timezone.now)
client = models.ForeignKey('Client',on_delete=models.PROTECT)
def total(self):
return round(self.invoiceitem_set.aggregate(
sum=Sum(F('price') * F('quantity'))
)['sum'] or 0, 2)
With an F object, you refer to a field with that name.

Difference between two Foreign key model fields

I have a model named 'CarAdd', and two other models are connected with a foreign key to this CarAdd model that are ShippingDetails, MaintenanceDetails. and each foreign key model has an item and price field." foreign key have more than 2 items under one id"
I need to find the profit amount of each car
profit = sold_amount - (purchased amount + total shipping cost + total maintenance cost)
class CarAdd(models.Model):
# Car Details
name = models.CharField(max_length=60)
purchased_amount = models.DecimalField()
# there 3 choices in STATUS shipped, maintenance, sold
status = models.CharField(max_length=12, choices=STATUS)
sold_amount = models.DecimalField(max_digits=15, decimal_places=1)
profit_amount = models.DecimalField(max_digits=15, decimal_places=1)
def save(self, *args, **kwargs):
if self.status == 'sold':
sum_invested = self.purchased_amount + ShippingDetails.price + MaintenanceDetails.price
profit = self.sold_amount - sum_invested
self.profit_amount = profit
super().save(*args, **kwargs)
class ShippingDetails(models.Model):
car = models.ForeignKey(CarAdd, related_name='shipping_details', on_delete=models.CASCADE)
item = models.CharField(max_length=60)
price = models.DecimalField(max_digits=15, decimal_places=1,)
class MaintenanceDetails(models.Model):
car = models.ForeignKey(CarAdd, related_name='maintenance_details', on_delete=models.CASCADE)
item = models.CharField(max_length=60)
price = models.DecimalField(max_digits=15, decimal_places=1)
how do I calculate the profit amount if status is 'sold' and save to profit_amount field
You need to aggregate on the related models:
from django.db.models import Sum
from django.db.models.functions import Coalesce
sum_invested = self.purchased_amount
+ self.shipping_details.aggregate(total_price=Coalesce(Sum('price'), 0))['total_price']
+ self.maintenance_details.aggregate(total_price=Coalesce(Sum('price'), 0))['total_price']

Aggregate #property child fields?

In Django I have a model that looks like this:
class Invoice(models.Model):
invoice_number = models.CharField(max_length=20)
#property
def total_invoice_amount(self):
return self.invoiceline_set.aggregate(Sum('price_incl_vat'))['price_incl_vat']
class InvoiceLine(models.Model):
invoice = models.ForeignKey(Invoice)
price_per_unit = models.DecimalField(decimal_places=4, max_digits=10)
unit = models.ForeignKey(staticdata.models.Unit)
amount = models.DecimalField(decimal_places=4, max_digits=10)
vat_percentage = models.DecimalField(decimal_places=4, max_digits=10)
# Calculated fields
#property
def price_excl_vat(self):
return self.price_per_unit * self.amount
#property
def vat_amount(self):
return self.price_excl_vat * self.vat_percentage / 100
#property
def price_incl_vat(self):
return self.price_excl_vat + self.vat_amount
I'd like to have a total invoice amount which is an aggregated sum of the computed field price_incl_vat. So I constructed total_invoice_amount like this:
#property
def total_invoice_amount(self):
return self.invoiceline_set.aggregate(Sum('price_incl_vat'))['price_incl_vat']
But apparently that doesn't work:
Cannot resolve keywork 'price_incl_vat' into field.
Any idea how I can achieve this?
It seems this is not directly possible with the aggregate function, so I solved it this way:
#property
def total_invoice_amount(self):
total = 0
for invoice_line in self.invoiceline_set.all():
total += invoice_line.price_incl_vat
return total