djangodjango-signals

post_save seams has bug


my simplified problem is below:

i have a model like this:

class Alert(models.Model):
    ALERT_CHOICES = [("email","email"),("sms","sms"),("notif","notif")]
    name = models.CharField(max_length = 50)
    owner = models.ForeignKey(User , on_delete=models.CASCADE)
    coins = models.ManyToManyField(Coin,blank=True)

when an object of Alert like alert changes. i want to print number of coins that connected to alert after change in this method:

@receiver(post_save, sender=Alert)
def handle_alert_save(sender, instance,  **kwargs):
    coins = instance.coins.all()
    print(len(coins))

Unfortunately! it prints number of coin before change alert. i want after change the object. in other words post_save signal is exactly equal to pre_save.

for example when i have 2 connected coin to aler and i change it to 5. it prints 2!

i try all other signals like pre_save and pre_delete and pre_migrate and post_migrate.


Solution

  • No, it does trigger the signal after saving the Alert, but the many-to-many fields are saved after saving the Alert, so at that time, there are indeed still no coins updated. This is one of the many reasons not to use signals in the first place [django-antipatterns].

    You could use the m2m_changed signal [Django-doc] instead:

    from django.db.models.signals import m2m_changed
    
    
    @receiver(m2m_changed, sender=Alert.coins.through)
    def handle_alert_save(sender, instance, **kwargs):
        coins = instance.coins.all()
        print(len(coins))

    But personally, I think signals are often not a good idea and should be used as last resort. It might be more robust to simply add a function to do the logic, and trigger this in the views where you alter the .coins of an Alert instance.