Reputation: 3018
I have a field in a model that will be calculated based on two values. The problem now all my code is done using manual SUM
or AVG
What I am trying to do is to have another way to design the model, and in the same time keep the same logic. The main reason I want to do this, because I need to take advantage of the djagno aggregate and annotate functions. Also, to calculate these values in the database side not in Python.
class Employee(models.Model):
name = models.CharField(....)
..
salary = models.FloatField(....)
class Benefit(models.Model):
employee = models.ForeignKey('Employee')
# this field can be
# Percentage (ex %2 from employee main salary)
# Fixed Price (ex $150)
# days
calculation_type = thetopchoices(FIX,PER,DAYS)
# this field should be the value
# of the percentage or the fixed amount
custom = models.PositiveIntegerField(....)
So basically, I have method that will check the calculation type and based on it I get the amount of the benefit.
def calculate(self):
if self.calculation_type == 'PER':
return self.employee.salary * self.custom
elif self.calculation_type == 'FIX':
return self.custom
else
return ( self.employee.salary / 30 ) * self.custom
@property
def amount(self):
return self.calculate()
Now the problem how to calculate the total of benefits for each employee. The best solution is to do the calculation in the database by using Django aggregation. However, using this design will never works because Django need the amount to be a real field.
My solution so far, but I don't think it's efficient.
def get_total_amount_of_benefits(self):
total = 0
for b in employee.benefit_set.all():
total += b.amount
return total
Upvotes: 0
Views: 169
Reputation: 39709
I think you should redefine the Benefit
model, add amount
field which will be automatically calculated based on calculation_type
and custom
fields value in model save
:
class Benefit(models.Model):
employee = models.ForeignKey('Employee')
# this field can be
# Percentage (ex %2 from employee main salary)
# Fixed Price (ex $150)
# days
calculation_type = thetopchoices(FIX,PER,DAYS)
# this field should be the value
# of the percentage or the fixed amount
custom = models.PositiveIntegerField(....)
amount = models.PositiveIntegerField(...., editable=False) # auto calculated field in save
def __init__(self, *args, **kwargs):
super(Benefit, self).__init__(*args, **kwargs)
self.__original_calc_type = self.calculation_type
self.__original_custom = self.custom
def calculate(self):
employee = self.employee
if self.calculation_type == 'PER':
amount = employee.salary * self.custom
elif self.calculation_type == 'FIX':
amount = self.custom
else
amount = ( employee.salary / 30 ) * self.custom
return amount
def save(self, *args, **kwargs):
recalculate = kwargs.pop('recalculate', False)
if self.__original_calc_type != self.calculation_type or self.__original_custom != self.custom or recalculate:
self.amount = self.calculate()
super(Benefit, self).save(*args, **kwargs)
self.__original_calc_type = self.calculation_type
self.__original_custom = self.custom
Now to get the amount of all benefits is easy:
from django.db.models import Sum
class Employee(models.Model):
...
...
@property
def benefits_amount(self):
d = self.benefit_set.aggregate(total_amount=Sum('amount'))
return d['total_amount'] if d['total_amount'] else 0
If you want to update the benefits later based on any criteria you have, you need to do it like this:
for benefit in employee.benefit_set.all():
benefit.save(recalculate=True)
Upvotes: 1