how to override django save method to update some field dynamically?
Solution One:
I think instead of changing in Ledger
model, you should change in Expense
model, like this:
class Expense(models.Model):
...
def save(self, *args, **kwargs):
self.payment_option.amount_to_pay = self.payment_option.amount_to_pay + self.amount_to_pay
self.payment_option.save()
super(Expense, self).save(*args, **kwargs)
Solution Two:
But to be honest, Solution One does not seem good to me. Reason is that you are saving same data in 2 places(in both expense and ledger). Instead, it should be once, then the amount_to_pay
value in Ledger
should be calculated dynamically. Like this:
from django.db.models import Sum
class Ledger(...):
@property
def amount_to_pay(self):
# I am using a property method to show the amount_to_pay value.
# FYI: in this way, you need to remove amount_to_pay field from Ledger model
return self.opening_balance - self.expense_set.all().aggregate(a_sum=Sum('amount_to_pay')).get('a_sum', 0)
In that way, with each ledger, the value amount_to_pay
will be dynamically calculated at runtime. For example:
for l in Ledger.objects.all():
l.amount_to_pay
Solution Three:
If you are wary of making DB hits with each l.amount_to_pay
(as it calculates amount_to_pay from DB dynamically) from previous solution, then you can always annotate
the value. Like this:
For this solution, you need to change your Expense
model and add a related_name
:
class Expense(models.Model):
pay_from = models.CharField(max_length=200)
payment_option = models.ForeignKey('Ledger', on_delete=models.CASCADE, related_name='expenses')
Then use that related_name
in query like this(FYI: You can't keep def amount_to_pay(...)
method in Ledger model for the following usage example):
from django.db.models import Sum, F, ExpressionWrapper, IntegerField
ledgers = Ledger.objects.all().annotate(expense_sum=Sum('expenses__amount_to_pay')).annotate(amount_to_pay=ExpressionWrapper(F('opening_balance') - F('expense_sum'), output_field=IntegerField()))
# usage one
for l in ledgers:
l.amount_to_pay
# usage two
ledgers.values('amount_to_pay')