#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 django-updates+unsubscr...@googlegroups.com.
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