Opened 16 months ago
Last modified 16 months ago
#34733 closed New feature
m2m_changed signal is unaware if .set() method is being called — at Initial Version
Reported by: | Leif Kjos | Owned by: | nobody |
---|---|---|---|
Component: | Database layer (models, ORM) | Version: | dev |
Severity: | Normal | Keywords: | |
Cc: | Triage Stage: | Unreviewed | |
Has patch: | no | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
When a related_m2m_field.set()
method is called, the m2m_changed
signal is fired four times for action: pre_remove
, post_remove
, pre_add
, and post_add
(or pre_clear
/ post_clear
).
This poses a problem if the signal is supposed to run validation on the final state of the model. For example, let's say I have a model called Customer
with a many-to-many relation to SubscriptionPlan
and I have a m2m_changed
signal that is supposed to validate if their SubscriptionPlan is valid.
If I call subscription_plans.set([some_plans])
, the many-to-many manager will first call subscription_plans.remove()
then .add()
inside an atomic transaction. If my signal validates on the first remove()
, it could be in an invalid state, even though it won't be by the time the .add()
completes.
To get around this, I had to create a custom ManyToManyField with a custom RelatedManager that set an instance variable on the model when the .set()
method was called. I'd like to propose adding a feature to the RelatedManager or to the signals to make the m2m_changed
signal aware of if the .set()
method was called when running. This could be a private attribute on the instance or extra information passed to the signal receiver.
If this feature already exists or there's a decent workaround, let me know and i'll close the ticket! Otherwise, I have a patch in mind that I can raise a PR for and attach.