#18094: `pre_delete` and `post_delete` signals are not correctly sent in
inheritance scenarios involving proxy models
-------------------------------------+-------------------------------------
     Reporter:  charettes            |      Owner:  nobody
         Type:  Bug                  |     Status:  new
    Component:  Database layer       |    Version:  1.4
  (models, ORM)                      |   Keywords:  model proxy multi-table
     Severity:  Normal               |  inheritance signals delete
 Triage Stage:  Unreviewed           |  Has patch:  0
Easy pickings:  0                    |      UI/UX:  0
-------------------------------------+-------------------------------------
 This is a follow up of #18083.

 Given the following models and signals (as of r17887):

 {{{#!python

 from django.db import models
 from django.db.models import signals


 class A(models.Model):
     pass

 class AProxy(A):
     class Meta:
         proxy = True

 class B(A):
     pass

 class BFirstProxy(B):
     class Meta:
         proxy = True

 class BSecondProxy(B):
     class Meta:
         proxy = True

 class BFirstProxyFirstProxy(BFirstProxy):
     class Meta:
         proxy = True

 class BFirstProxySecondProxy(BFirstProxy):
     class Meta:
         proxy = True


 def pre_delete(sender, instance, **kwargs):
     print "`pre_delete` %r %r" % (sender, instance)

 signals.pre_delete.connect(pre_delete, A)
 signals.pre_delete.connect(pre_delete, AProxy)
 signals.pre_delete.connect(pre_delete, B)
 signals.pre_delete.connect(pre_delete, BFirstProxy)
 signals.pre_delete.connect(pre_delete, BSecondProxy)
 signals.pre_delete.connect(pre_delete, BFirstProxyFirstProxy)
 signals.pre_delete.connect(pre_delete, BFirstProxySecondProxy)

 def post_delete(sender, instance, **kwargs):
     return # Avoid dispatching for output clarity
     print "`post_delete` %r %r" % (sender, instance)

 signals.post_delete.connect(post_delete, A)
 signals.post_delete.connect(post_delete, AProxy)
 signals.post_delete.connect(post_delete, B)
 signals.post_delete.connect(post_delete, BFirstProxy)
 signals.post_delete.connect(post_delete, BSecondProxy)
 signals.post_delete.connect(post_delete, BFirstProxyFirstProxy)
 signals.post_delete.connect(post_delete, BFirstProxySecondProxy)
 }}}

 `pre_delete` and `post_delete` signals dispatching depends on the deletion
 source and don't propagate up the proxy chain nor to the proxy leaves.
 i.e. (here `post_delete`'s dispatching should behave the same)

 == Basic model proxy ==
 '''Actual behaviours'''
 {{{
 >>> A.objects.create().delete()
 `pre_delete` <class 'proxy_signals.models.A'> <A: A object>
 >>> AProxy.objects.create().delete()
 `pre_delete` <class 'proxy_signals.models.AProxy'> <AProxy: AProxy object>
 }}}

 '''Expected behaviour in both cases'''
 {{{
 `pre_delete` <class 'proxy_signals.models.AProxy'> <AProxy: AProxy object>
 `pre_delete` <class 'proxy_signals.models.A'> <A: A object>
 }}}

 == Multi-table inheritance model proxy ==
 '''Actual behaviours'''
 {{{
 >>> B.objects.create().delete()
 `pre_delete` <class 'proxy_signals.models.B'> <B: B object>
 `pre_delete` <class 'proxy_signals.models.A'> <A: A object>
 >>> BFirstProxy.objects.create().delete()
 `pre_delete` <class 'proxy_signals.models.BFirstProxy'> <BFirstProxy:
 BFirstProxy object>
 `pre_delete` <class 'proxy_signals.models.A'> <A: A object>
 >>> BSecondProxy.objects.create().delete()
 `pre_delete` <class 'proxy_signals.models.BSecondProxy'> <BSecondProxy:
 BSecondProxy object>
 `pre_delete` <class 'proxy_signals.models.A'> <A: A object>
 >>> BFirstProxyFirstProxy.objects.create().delete()
 `pre_delete` <class 'proxy_signals.models.BFirstProxyFirstProxy'>
 <BFirstProxyFirstProxy: BFirstProxyFirstProxy object>
 `pre_delete` <class 'proxy_signals.models.A'> <A: A object>
 >>> BFirstProxySecondProxy.objects.create().delete()
 `pre_delete` <class 'proxy_signals.models.BFirstProxySecondProxy'>
 <BFirstProxySecondProxy: BFirstProxySecondProxy object>
 `pre_delete` <class 'proxy_signals.models.A'> <A: A object>
 }}}

 '''Expected behaviour in all cases'''
 {{{
 `pre_delete` <class 'proxy_signals.models.BFirstProxyFirstProxy'>
 <BFirstProxyFirstProxy: BFirstProxyFirstProxy object>
 `pre_delete` <class 'proxy_signals.models.BFirstProxySecondProxy'>
 <BFirstProxySecondProxy: BFirstProxySecondProxy object>
 `pre_delete` <class 'proxy_signals.models.BFirstProxy'> <BFirstProxy:
 BFirstProxy object>
 `pre_delete` <class 'proxy_signals.models.BSecondProxy'> <BSecondProxy:
 BSecondProxy object>
 `pre_delete` <class 'proxy_signals.models.B'> <B: B object>
 `pre_delete` <class 'proxy_signals.models.AProxy'> <AProxy: AProxy object>
 `pre_delete` <class 'proxy_signals.models.A'> <A: A object>
 }}}

 What needs to be fixed:
 * Dispatch the `concrete_model` signals even when the source is an
 instance of a proxy
 * Dispatch the signals for all proxy of concrete models involved in the
 deletion
 * Preserve the expected dispatching order

 Failing integrated TestCase to come.

-- 
Ticket URL: <https://code.djangoproject.com/ticket/18094>
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 post to this group, send email to django-updates@googlegroups.com.
To unsubscribe from this group, send email to 
django-updates+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/django-updates?hl=en.

Reply via email to