Reputation: 11
I want to have the amount under Rent to increment on the amount under Accounts. When a user types in the amount in Rent it should deduct or add in the amount in Account and should reset if paid or stay the same when not paid.
class Account(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=250)
number = models.PositiveIntegerField(help_text="Account number", unique=True, blank=True, null=False)
currency = models.CharField(max_length=3, choices=ALLOWED_CURRENCIES)
creation_date = models.DateTimeField(auto_now_add=True)
amount = models.DecimalField(max_digits=MONEY_MAX_DIGITS, decimal_places=MONEY_DECIMAL_PLACES, help_text="Latest" "balance", default=0, blank=False, null=True)
class Rent(models.Model):
source = models.ForeignKey(Account, on_delete=models.CASCADE, verbose_name="Account holder.")
amount = models.DecimalField(max_digits=MONEY_MAX_DIGITS, decimal_places=MONEY_DECIMAL_PLACES)
date = models.DateTimeField(auto_now_add=True)
Upvotes: 1
Views: 38
Reputation: 17751
You can use one of these three strategies:
Compute the Account's amount dynamically. Instead of using the amount
field, you can use a method (or maybe a cached property) like this:
from django.db.models import Sum
class Account(models.Model):
# ...
def amount(self):
result = self.rent_set.all().aggregate(
total_amount=Sum('amount'))
return result['total_amount']
This approach has the advantage that it is always accurate: no matter how you create and manipulate Account and Rent records, this method will always return the correct amount.
It has the disadvantage of issuing a query every time you call it. This will have a performance impact.
Update the Account amount field when Rent is saved or deleted:
from django.db.models import Sum
class Account(models.Model):
# ...
def update_amount(self):
result = self.rent_set.all().aggregate(
total_amount=Sum('amount'))
self.amount = result['total_amount']
class Rent(models.Model):
# ...
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
self.account.update_amount()
self.account.save()
def delete(self, *args, **kwargs):
account = self.account
super().delete(*args, **kwargs)
account.update_amount()
account.save()
This approach has the advantage that the amount is stored into Account records, and therefore does not need to be recalculated every time you need it, only when a Rent object is saved.
The disadvantage is that you must call save()
and delete()
on Rent objects. You cannot use, for example, queryset methods like Rent.objects.all().delete(...)
or Rent.objects.all().update(...)
because your custom save()
and delete()
won't be called.
Another issue is that the amount won't be updated if you edit/delete Rent records at the database level, without going through your custom code.
Update the Account amount upon receiving post_save
/post_delete
signals (thanks user3375448 for the suggestion):
from django.db.models.signals import post_save, post_delete
from django.dispatch import receiver
@receiver(post_save, sender=Rent)
@receiver(post_delete, sender=Rent)
def my_handler(sender, instance, **kwargs):
instance.account.update_amount()
This is essentially like the previous solution and it has essentially the same advantages and disadvantages.
One improvement from the previous solution is that post_delete
is sent when a queryset is deleted as well.
Upvotes: 1