Can I detect ManyToManyField mutations before they are applied?

Hello Django Fellows.
Being fairly new to Django, I ran across an issue recently which is driving me nuts. I figured i might use some help there. To quote my question on stack overflow :

Can I detect ManyToManyField mutations before they are applied?

I have a Django app that broadcasts changes of some of its models to its clients, to keep them up-to-date.
I have a separate app for that, that binds to the post_save signal of these models and triggers the broadcast. (btw: i am aware that over-using signals is an anti-pattern, but in this case it seems like a good way for me to keep coupling low between the “live” app and the other apps)

Problems have come with models with ManyToManyFields. I am not very familiar with Django’s handling of this but I understand that these fields are actually updated after the model is saved, due to the underlying relation table.

I was able to use the m2m_changed signal to react to the post_* actions and dispatch the broadcast after the object is all up-to-date. However, in the post_save handler, I still need to detect that the m2m field is going to be mutated, to avoid broadcasting incomplete data and confusing the clients. How can I detect this , either in the post_save signal handler or the model’s save method ? Is there a way to raise a flag on an object when a m2m field is about to be mutated ?

Here’s what I’ve tried :

  • Handle the pre_* actions of the m2m_changed signal to detect incoming mutation of the field but that does not work, because the signal gets fired after post_save, which is too late (incomplete data has already been broadcasted).
  • Store initial values when the model instance is created, and compare them in the overriden save method to look for changes. This does not work because the fields are not changed (yet) and report to be == the initial value ; plus I read on other questions that this practice could cause race conditions.

I’m looking forward for your help. Thanks a lot in advance.

To make sure we’re on the same page -

A ManyToMany relationship only physically exists as the third table joining two other tables. (e.g. Book and Author joined by a BookAuthor table.)

Changing which books were written by which authors does not involve a any change to either Book or Author - only the presence or absence of entries in that BookAuthor table.

Note that those changes don’t require anything be changed in either the Book or Author model. You can make those changes directly in the BookAuthor model.

I point this out to say that these are all separate and independent events. For any given set of updates, you may have changes made to any combination of Book, Author, and BookAuthor.

So, if you’re looking to consolidate notifications, I’d ditch the signals and create a specific “update handler” to be used by whatever view(s) are performing these updates. That handler would have the ability to track a more “global” perspective of the changes being made to more properly create your notifications.
If you’re really committed to using the signals infrastructure, you can expose your own signal with that consolidated information.

KenWhitesell,

Thank you very much for your quick reply. And sorry for mine being so late.

It took me some time, but as you suggested I re-implemented the notification system at the view level instead of doing it in the model layer. I made a mixin that adds notification features to my views.
Thanks to this solution :

  • I got rid of signals completely
  • I got much better control over what’s going on, which simplified the notification handler code quite a bit.

Thank you very much for your help, your advice was spot on. :slight_smile:

Regards

1 Like