Search code examples
pythondjangodjango-modelsdjango-signals

Avoiding duplicate signals in Django when editing many_to_many fields


I want automatically update an external application about a change in a model. The problem is that the data is in a many2many relation between events <-> users. I tried to use the "m2m_changed" signal.

@receiver(m2m_changed, sender=models.Event.organisers.through)
def event_changed(sender, instance, action, *args, **kwargs):
    if "post" in action:
      # hey api here is the new list of organisers of this

The problem with this is that if i make a single change where i remove one user and add another then this code is called twice! That's no good and i can't just ignore one type of operation in case only that operation is called. I have thought pushing instances to stacks and ignoring dups but that seems messy. Is there a way i can make my own signal that only fires once?


Solution

  • There doesn't relay seem to be a good answer to this question so here is some useful workarounds that are better than what i was imagined in the first pace.

    Solution 1:

    Instead of combining signals instead add the primary keys of the instances to a set to ignore duplicate signals:

    updated = set()
    
    @receiver(m2m_changed, sender=models.Event.organisers.through) 
    def event_changed(sender, instance, action, *args, **kwargs):
            if "post" in action:
              updated.add(instance.pk)
    
    def send_updates():
        for Event in updated:              # Iteration AKA for each element
           #update code here
    

    While this requires some sort of scheduled task to run send_updates() it avoids the chance of spamming if there are many consecutive changes to the event.

    Solution 2

    Ignore signals altogether add last modified to the model. Then run a query to get all events between now and when the send_updates() was last called. Store the last called somewhere to disk/database to avoid having to resend everything on restart.