#34733: m2m_changed signal is unaware if .set() method is being called
-------------------------------------+-------------------------------------
     Reporter:  lekjos               |                    Owner:  lekjos
         Type:  New feature          |                   Status:  assigned
    Component:  Database layer       |                  Version:  dev
  (models, ORM)                      |
     Severity:  Normal               |               Resolution:
     Keywords:                       |             Triage Stage:
                                     |  Unreviewed
    Has patch:  0                    |      Needs documentation:  0
  Needs tests:  0                    |  Patch needs improvement:  0
Easy pickings:  0                    |                    UI/UX:  0
-------------------------------------+-------------------------------------
Changes (by lekjos):

 * owner:  nobody => lekjos
 * status:  new => assigned


Old 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.

New 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 `Customer.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. However, I still need to be able to
 validate on the signal if the standard `.remove()` method is called.

 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.

--

-- 
Ticket URL: <https://code.djangoproject.com/ticket/34733#comment:1>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/010701897aabd72d-9cd38089-b857-48dd-a2d4-eb0d7f72f25a-000000%40eu-central-1.amazonses.com.

Reply via email to