uchiha itachi
uchiha itachi

Reputation: 195

Change value of a model based on value of other model Django

I have a models Wallet and StudentPayment. Every time I add or update StudentPayment, the Wallet balance should change based on the added or updated StudentPayment.

class Wallet(models.Model):
    ...
    balance = models.DecimalField(decimal_places=2, max_digits=100, default=0.00)
    ...


class StudentPayment(models.Model):
    ...
    wallet = models.ForeignKey(
                 Wallet, 
                 on_delete=models.SET_NULL, 
                 null=True, related_name='students_payment')

    amount = models.DecimalField(decimal_places=2, max_digits=100)
    ...

For Example: if I add payment with 1000 amount, the wallet balance should change to 1000 too, I know how to do this, but don't know how to deal with the update, ex: if I change the payment amount to 900, the wallet balance should also change. Appreciate any help)

I tried to implement with overriding save() method, but nothing works

Upvotes: 0

Views: 60

Answers (1)

markwalker_
markwalker_

Reputation: 12869

Signals should suit what you want here. Signals are fired after certain actions, like saving or deleting objects so you can execute a function when a StudentPayment is saved or deleted.

At that point you probably want the Wallet balance to be the total of all amounts for payments to that wallet.

    from django.db.models import Sum
    from django.db.models.signals import (
        post_delete,
        post_save,
    )
    from django.dispatch import receiver


    class Wallet(models.Model):
        ...
        balance = models.DecimalField(decimal_places=2, max_digits=100, default=0.00)
        ...
    

    class StudentPayment(models.Model):
        ...
        wallet = models.ForeignKey(
                     Wallet, 
                     on_delete=models.SET_NULL, 
                     null=True, related_name='students_payment')
    
        amount = models.DecimalField(decimal_places=2, max_digits=100)
        ...


    @receiver([post_save, post_delete], sender=StudentPayment)
    def calculate_total_amount(instance, **kwargs):
     
        wallet = instance.wallet

        # Add together the amount of all `StudentPayment` objects for the wallet
        total = StudentPayment.objects.filter(wallet=wallet).aggregate(
            Sum('amount')
        )['amount__sum']

        wallet.balance = total
        wallet.save(update_fields=['balance'])

Some further reading for you on aggregation and signals

Upvotes: 1

Related Questions