Tu Le Thanh
Tu Le Thanh

Reputation: 661

Django: How to update a related model when create an other model

I have 2 models: Transaction and Wallet, a wallet has multiple transactions. I need to update the amount field in Wallet model whenever a new transaction is created. The way I see is overwrite save method in the model. Currently, I wrote like this

def save(self, *args, **kwargs): 
    if self.kind == "Income":
        self.wallet.amount += self.amount
    elif self.kind == "Outcome":
        self.wallet.amount -= self.amount
    self.wallet.save()
    super(Transaction, self).save(*args, **kwargs)

It creates a new transaction correctly but not update the wallet model. How I can fix that?

Upvotes: 0

Views: 236

Answers (3)

Ariel D
Ariel D

Reputation: 16

Did you check to see that your conditional statements are getting triggered?

This should actually work without you having to call self.wallet.save() if you have already passed a valid Wallet object to the Transaction.

def save(self, *args, **kwargs): 
    if self.kind == "Income":
        self.wallet.amount += self.amount
    elif self.kind == "Outcome":
        self.wallet.amount -= self.amount
    super(Transaction, self).save(*args, **kwargs)

See: https://docs.djangoproject.com/en/3.1/topics/db/queries/#saving-changes-to-objects

For information on how to save Object references.

So I think two things could be going wrong. Either your conditional statements are not getting triggered, or you are not passing a valid Wallet Object reference to the Transaction.

Either way...I think this is a good use case for Django's signals. First, I would remove the save method you have, and in the Transaction model I would add

def update_transaction_wallet(sender, instance, **kwargs):
    if instance.kind == "Income":
        instance.wallet.amount += instance.amount
    elif instance.kind == "Outcome":
        instance.wallet.amount -= instance.amount

signals.post_save.connect(update_transaction_wallet, sender=Transaction)

You may have to tweak this a bit in order for it to work in your specific case. You didn't provide a lot of information about your models and situation.

But basically, this bit of code tells Django that whenever a Transaction Objects gets saved to also run the update_transaction_wallet() method. See: https://docs.djangoproject.com/en/3.0/topics/signals/

Upvotes: 0

You should not be doing this because that information can be accessed via aggregate functions. Check out on django documentation.

But, if for some specific reason you you need to do it, you need to do it after the transaction is saved, with F expression:

from django.db.models import F

def save(self, *args, **kwargs): 
    super(Transaction, self).save(*args, **kwargs)
    if self.kind == "Income":
        self.wallet.amount = F('amount') + self.amount
    elif self.kind == "Outcome":
        self.wallet.amount = F('amount') - self.amount
    self.wallet.save()

Upvotes: 1

Carlos Leite
Carlos Leite

Reputation: 302

You have to instantiate Wallet and then make the proper addition or subtraction on it.

I would do it inside a Transaction method, which you will call on save or any other place. and if saves fail, rowback the wallet amount too ;)

Upvotes: 0

Related Questions