Aggregate #property child fields? - django

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

Related

Django how the link ManytoManyField data with FloatField data?

I have manytomanyfield inside my model.The manytomanyfield field lists the products in the products table.
I want to enter the amount for each product I choose. How can I relate manytomanyfield to floatfield field?
That's my model:
`
class TaskSources(models.Model):
id = models.AutoField(primary_key=True)
user_task_id = models.ForeignKey(UserTask,on_delete=models.CASCADE)
product_id = models.ManyToManyField(Product, verbose_name="Product",default=None)
product_amount = models.FloatField(max_length=255,verbose_name="Product Amount")
`
The form:
`
class TaskSourcesForm(forms.ModelForm):
class Meta:
model = TaskSources
fields = ['product_id', 'product_amount']
`
The views:
`
#login_required(login_url="login")
def addUserTask(request):
user_task_form = UserTaskForm(request.POST or None,initial={'user_id': request.user})
task_sources_form = TaskSourcesForm(request.POST or None)
if request.method == 'POST':
if user_task_form.is_valid():
user_task = user_task_form.save(commit=False)
user_task.author = request.user
user_task.save()
print(user_task.id)
if task_sources_form.is_valid():
task_sources = task_sources_form.save(commit=False)
task_sources.user_task_id = UserTask(id = user_task.id)
task_sources.save()
task_sources_form.save_m2m()
messages.success(request,"Task added successfully!")
return redirect(".")
context = {
"user_task_form" : user_task_form,
"task_sources_form" : task_sources_form,
}
return render(request,"user/addtask.html",context)
`
Thanks for care.
I tried associating the two fields with each other, but I could not succeed.
If I got it right I think that what you need is an intermediate table between your models. That way you can link an amount of a product to a TaskSource, something similar to this:
class Product(models.Model):
name = models.CharField(max_length=128)
def __str__(self):
return self.name
class ProductAmount(models.Model):
amount = models.FloatField(max_length=255,verbose_name="Product Amount")
product = models.ManyToManyField(Product, through='TaskSources')
def __str__(self):
return self.name
class UserTask(models.Model):
name = models.CharField(max_length=128)
def __str__(self):
return self.name
class TaskSources(models.Model):
id = models.AutoField(primary_key=True) # this is not really necessary
task = models.ForeignKey(UserTask,on_delete=models.CASCADE)
product_amount = models.ForeignKey(ProductAmount, on_delete=models.CASCADE)

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 Aggregation: Summation of Multiplication of two fields with ForienKey

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

Subtract models.DateField to get number of days

I have a simple model that tracks work leave requests:
class LeaveRequest(models.Model):
employee = models.ForeignKey(UserProfile)
supervisor = models.ForeignKey(UserProfile, related_name='+', blank=False, null=False)
submit_date = models.DateField(("Date"), default=datetime.date.today)
leave_type = models.CharField(max_length=64, choices=TYPE_CHOICES)
start_date = models.DateField(("Date"))
return_date = models.DateField(("Date"))
total_days = models.IntegerField()
notes = models.TextField(max_length=1000)
def __unicode__ (self):
return u'%s %s' % (self.employee, self.submit_date)
class Admin:
pass
class Meta:
ordering = ['-submit_date']
In the view I need a function to calculate the number of days requested. Secondarily, I'll need a method to count only weekdays, but for now I've got the following:
def leave_screen(request, id):
records = LeaveRequest.objects.filter(employee=id)
total_days = LeaveRequest.return_date - LeaveRequest.start_date
tpl = 'vacation/leave_request.html'
return render_to_response(tpl, {'records': records })
which produces a attribute error
type object 'LeaveRequest' has no attribute 'return_date
any suggestions?
In total_days, you are calling the model and not the instance of that model - records - that you created.
If you want to view just a single Leave record, you would need to pass the id of the LeaveRequest
def leave_screen(request, id):
records = LeaveRequest.objects.get(id=id)
total_days = records.return_date - records.start_date
tpl = 'vacation/leave_request.html'
return render_to_response(tpl, {'records': records })
The answer that suggests using it as a property will work but I think I'll prefer keeping it as a field and just computing it at the time of insert.
class LeaveRequest(models.Model):
employee = models.ForeignKey(UserProfile)
supervisor = models.ForeignKey(UserProfile, related_name='+', blank=False, null=False)
submit_date = models.DateField(("Date"), default=datetime.date.today)
leave_type = models.CharField(max_length=64, choices=TYPE_CHOICES)
start_date = models.DateField(("Date"))
return_date = models.DateField(("Date"))
total_days = models.IntegerField()
notes = models.TextField(max_length=1000)
def __unicode__ (self):
return u'%s %s' % (self.employee, self.submit_date)
def save(self, *args, **kwargs):
self.total_days = (self.return_date - self.start_date).days
super(LeaveRequest, self).save(*args, **kwargs)
class Admin:
pass
class Meta:
ordering = ['-submit_date']
This way when you put in the logic for excluding weekends you are saving computation to calculate the days everytime at the time of listing all leave requests.
I wouldn't have 'total_days' as a field in the LeaveRequest class, but rather as a property.
class LeaveRequest(models.Model):
(other fields)
#property
def total_days(self):
oneday = datetime.timedelta(days=1)
dt = self.start_date
total_days = 0
while(dt <= self.return_date):
if not dt.isoweekday() in (6, 7):
total_days += 1
dt += oneday
return totaldays
# view function
def leave_screen(request, id):
# get leave request by id
leavereq = LeaveRequest.objects.get(id=id)
return render_to_response("vacation/leave_request.html", {"leavereq": leavereq})
# template code
...
<body>
{{ leavereq.total_days }}
</body>