Can someone help me resolving this problem. I've created an django-app with two models. One model is an Wallet model and other is Transaction model. Every transaction is conneted to a wallet with models.ForeignKey. I've also created two signals, one is for updating cryptocurrency balance (eg. BTC, ETH) in Wallet when transaction is made and the other signal i want to update Total_Balance (which is all other balances converted to USD). And here I have a problem because my signal is POST_SAVE and in it I have a save() method which causes infinity loop. I think if I would do a whole thing in my first signal it would work but in future i want to add new model which also will be connected to Wallet and it would pop-up my total_balance, therefor i would need same logic in third signal and I would end up with same code in two signals.
I know my description is a little messy. Here is some code to help understand it.
My Models:
class Wallet(models.Model):
name = models.CharField(max_length=50)
total_balance = models.IntegerField(blank=True)
btc_balance = models.DecimalField(max_digits=15, decimal_places=8)
xrp_balance = models.DecimalField(max_digits=15, decimal_places=8)
eth_balance = models.DecimalField(max_digits=15, decimal_places=8)
class Transaction(models.Model):
wallet = models.ForeignKey(Wallet, on_delete=models.CASCADE)
currency_paid = models.CharField(choices=CURRENCY, max_length=3)
amount_paid = models.DecimalField(max_digits=15, decimal_places=8)
currency_recived = models.CharField(choices=CURRENCY, max_length=3)
amount_recived = models.DecimalField(max_digits=15, decimal_places=8)
My Signals:
@receiver(post_save, sender=Transaction)
def create_transaction(sender, instance, created, **kwargs):
if created:
wallet = Wallet.objects.get(name = instance.wallet)
currency_paid = instance.currency_paid
currency_recived = instance.currency_recived
amount_paid = instance.amount_paid
amount_recived = instance.amount_recived
# SUBSTRACK BALANCE
if(currency_paid == 'BTC'):
wallet.btc_balance -= amount_paid
...
# ADDS BALANCE
if(currency_recived == 'BTC'):
wallet.btc_balance += amount_recived
...
wallet.save()
@receiver(post_save, sender=Wallet)
def total_balance_update(sender, instance, created, **kwargs):
if created == False:
btc_price = cryptocompare.get_price('BTC',curr='USD')['BTC']['USD']
xrp_price = cryptocompare.get_price('XRP',curr='USD')['XRP']['USD']
...
btc_balance = float(instance.btc_balance)
xrp_balance = float(instance.xrp_balance)
...
total_balance = instance.total_balance
total_balance = round(btc_balance * btc_price) + round(xrp_balance * xrp_balance) + round(eth_balance * eth_balance)
instance.save()
The post_save
of your Wallet
performs an instance.save()
, so that means that if you .save()
your Wallet
, it will trigger the post_save
signal on the Wallet
that will then again save the wallet, and thus each time the signal runs it saves the wallet again triggering the signal.
As far as I can see, you do not need to use a post_save
signal however, since you do not seem to use anything only available after the object has been saved. You can use a pre_save
signal that will run before saving the object to the database:
from django.db.models.signals import pre_save
@receiver(pre_save, sender=Wallet)
def total_balance_update(sender, instance, **kwargs):
if instance.pk is not None:
btc_price = cryptocompare.get_price('BTC',curr='USD')['BTC']['USD']
xrp_price = cryptocompare.get_price('XRP',curr='USD')['XRP']['USD']
...
btc_balance = float(instance.btc_balance)
xrp_balance = float(instance.xrp_balance)
...
instance.total_balance = round(btc_balance * btc_price) + round(xrp_balance * xrp_balance) + round(eth_balance * eth_balance)
# no instance.save()
We thus do not need to save the instance
since immediately after running the signal, Django will start creating/updating the record at the database.