m2m_changed signal

70 views
Skip to first unread message

George Sakkis

unread,
Jun 14, 2010, 9:18:35 AM6/14/10
to Django developers
I'm wondering what was the rationale for introducing a new separate
signal for m2m relationships, as opposed to using the existing ones on
the intermediate ('through') model. Currently the situations is
confusing:

* For m2m fields with auto_created intermediate model, calling
``Model.m2m_field.add/remove/clear`` sends m2m_changed. However adding/
removing relations using directly the through model (e.g.
``Model.m2m_field.through.objects.create(...)) does not send
m2m_changed. Adding handlers for pre/post_save, pre/post_delete on the
through model has no effect; the code specifically checks for
auto_created models and does *not* send these signals in this case.

* For m2m fields with a given intermediate model,
``Model.m2m_field.add/remove`` are not exposed (at least for now but
this is under discussion) and therefore m2m_changed is not sent;
instead you have to handle addition/removal in pre/post_save, pre/
post_delete handlers on the through model. However
``Model.m2m_field.clear()`` is exposed and does send m2m_changed; if
there are pre/post_delete handlers on the through model, they are
called as well after the m2m_changed.

If nothing else, this creates an additional discrepancy between
implicit and explicit through models. Switching from one to the other
requires porting all the related signal handlers (a non-trivial task
since m2m_changed has quite different API) and hope for the best. It
also makes things harder for library code that attempts to work on
arbitrary m2m fields, which normally wouldn't (or shouldn't) care
whether the through model is auto_created or not. Have these issues
been raised before ? If so, what's the suggested way to use signals
with m2m relations ?

George

Russell Keith-Magee

unread,
Jun 14, 2010, 9:38:59 AM6/14/10
to django-d...@googlegroups.com
On Mon, Jun 14, 2010 at 9:18 PM, George Sakkis <george...@gmail.com> wrote:
> I'm wondering what was the rationale for introducing a new separate
> signal for m2m relationships, as opposed to using the existing ones on
> the intermediate ('through') model.

The normal model save/delete signals were disabled because of the
potential for signal flood. If you add 100 objects to an m2m relation,
you would have received 100 signals. This is a time consuming activity
-- sending a signal consumes resources, even if it has no receivers;
and as soon as you *do* connect a receiver, you consume even more
resources. In the interests of performance, individual object signals
on m2m operations are disabled in favor of the grouped m2m_changed
signal.

> * For m2m fields with auto_created intermediate model, calling
> ``Model.m2m_field.add/remove/clear`` sends m2m_changed. However adding/
> removing relations using directly the through model (e.g.
> ``Model.m2m_field.through.objects.create(...)) does not send
> m2m_changed. Adding handlers for pre/post_save, pre/post_delete on the
> through model has no effect; the code specifically checks for
> auto_created models and does *not* send these signals in this case.

This sounds like an oversight to me.

> * For m2m fields with a given intermediate model,
> ``Model.m2m_field.add/remove`` are not exposed (at least for now but
> this is under discussion) and therefore m2m_changed is not sent;
> instead you have to handle addition/removal in pre/post_save, pre/
> post_delete handlers on the through model. However
> ``Model.m2m_field.clear()`` is exposed and does send m2m_changed; if
> there are pre/post_delete handlers on the through model, they are
> called as well after the m2m_changed.

> If nothing else, this creates an additional discrepancy between
> implicit and explicit through models. Switching from one to the other
> requires porting all the related signal handlers (a non-trivial task
> since m2m_changed has quite different API) and hope for the best. It
> also makes things harder for library code that attempts to work on
> arbitrary m2m fields, which normally wouldn't (or shouldn't) care
> whether the through model is auto_created or not.

Agreed, this certainly appears to be a inconsistency that needs to be
cleaned up. Moving to a manually defined through model shouldn't
require massive rewrites of signal handlers.

> Have these issues
> been raised before ? If so, what's the suggested way to use signals
> with m2m relations ?

No - these issues haven't been raised before (at least, not to my
knowledge). If you could open tickets for these issues, I'd be much
obliged. I'd be even more obliged if you'd try your hand at patches
:-)

Yours,
Russ Magee %-)

George Sakkis

unread,
Jun 14, 2010, 10:29:48 AM6/14/10
to Django developers
On Jun 14, 3:38 pm, Russell Keith-Magee <russ...@keith-magee.com>
wrote:

> On Mon, Jun 14, 2010 at 9:18 PM, George Sakkis <george.sak...@gmail.com> wrote:
> > I'm wondering what was the rationale for introducing a new separate
> > signal for m2m relationships, as opposed to using the existing ones on
> > the intermediate ('through') model.
>
> The normal model save/delete signals were disabled because of the
> potential for signal flood. If you add 100 objects to an m2m relation,
> you would have received 100 signals. This is a time consuming activity
> -- sending a signal consumes resources, even if it has no receivers;
> and as soon as you *do* connect a receiver, you consume even more
> resources. In the interests of performance, individual object signals
> on m2m operations are disabled in favor of the grouped m2m_changed
> signal.

I see, it makes sense for batch additions and removals.
The first part was easy :-) http://code.djangoproject.com/ticket/13757.
As for a patch, I haven't thought of how a proper solution would look
like at this point; if you have any ideas feel free to post them here
or the ticket page.

George
Reply all
Reply to author
Forward
0 new messages