Ziad Ahmed
Ziad Ahmed

Reputation: 74

Django signals doesn't update related model when foreign key updated

I want to update a related model every time a foreign key is changed using signals, similar to m2m_changed but for a foreign key.

User model:

class User(AbstractUser):
    balance = models.FloatField(default=0)

Transaction model:

class Transaction(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='transactions')
    order = models.OneToOneField('Payment.Order', on_delete=models.CASCADE, related_name='transaction')
    transaction_type = models.CharField(choices=[
        ('transfer', 'Transfer'),
        ('received', 'Received'),
    ], max_length=20, default='transfer')
    created = models.DateTimeField(auto_now_add=True)

I need that when a new transaction is created under the User, it's collected to the balance.

Signal code:

@receiver(pre_save, sender=User)
    def collect_transaction(sender, instance, **kwargs):
    balance = instance.transactions.aggregate(Sum('order__price'))
    print(balance)

but it works only when I update it from the User model, and not Transaction.

For example, this code will make the signals work:

user.transactions.add(transaction)
user.save()

Whereas this code won't make the signals work:

Transaction.objects.create(user=1, order=1, transaction_type='received')

Upvotes: 3

Views: 1451

Answers (1)

Mihai Chelaru
Mihai Chelaru

Reputation: 8207

Your signal will only be sent when the User model is saved. It seems you want it to be sent when any Transaction is saved, so you should try using Transaction as the sender instead of User:

@receiver(post_save, sender=Transaction)
def collect_transactions(sender, instance, **kwargs):
    user = instance.user
    user.balance = user.transactions.aggregate(Sum('order__price'))
    user.save()

As for your original example of having a signal when User is saved, I'm trying to think of a case where you'd be updating User but not updating its related transactions and yet need the balance to be updated. If there does exist such a case, you might just be better off overriding the save() method on the User model.

Upvotes: 1

Related Questions