Changes between Initial Version and Version 6 of Ticket #24738


Ignore:
Timestamp:
May 2, 2015, 5:26:44 PM (9 years ago)
Author:
No-0n3
Comment:

Legend:

Unmodified
Added
Removed
Modified
  • Ticket #24738

    • Property Version 1.8master
    • Property Cc No-0n3 added
  • Ticket #24738 – Description

    initial v6  
    1 Hi,
     1Hi again,
    22
    3 Have observed that no m2m_changed signal is sent when a related object is deleted.
     3I looked at #6707 and it isn't a problem about M2M in admin I'm talking about. Installed the master branch from github and tested.
    44
    5 If I explicitly clear the relations with "clear()" or remove the relation with "remove()", m2m_changed is correctly sent but if I delete it without calling "clear()" or "remove()" on the m2m field no signal is sent to indicate a change in relationship.
     5I'm trying to delete all objects from the database that doesn't have any relation by signaling that the relationship has been changed with the "m2m_changed" signal.
    66
    7 Is this by design or should a m2m_changed signal be sent when a related object is deleted from the m2m table at deletion when the relations also are removed.
     7If I delete a model through the shell with "<object>.delete()", then no "m2m_changed" signal is sent that any relations has been changed in any way.
    88
    9 Right now I've done a workaround by catching the "pre_delete" signal on my model that explicitly calls "clear()" on the M2M field to trigger the signal and catching it with my other signal function when it gets a "post_clear" action in m2m_changed signal.
     9Here is my test:
     10{{{
     11Python 2.7.9 (default, Dec 12 2014, 07:23:35)
     12[GCC 4.9.2] on linux2
     13Type "help", "copyright", "credits" or "license" for more information.
     14(InteractiveConsole)
     15>>> from blacklist.models import Blacklist, Record
     16>>> l = Blacklist.objects.create(name="test")
     17>>> r = Record.objects.create(ip="127.0.0.2")
     18>>> r.blacklists.add(l)
     19{'reverse': False, 'signal': <django.db.models.signals.ModelSignal object at 0x7606ad30>, 'instance': <Record: 127.0.0.2>, 'pk_set': set([10L]), 'using': 'default', 'model': <class 'blacklist.models.Blacklist'>, 'action': u'pre_add'}
     20{'reverse': False, 'signal': <django.db.models.signals.ModelSignal object at 0x7606ad30>, 'instance': <Record: 127.0.0.2>, 'pk_set': set([10L]), 'using': 'default', 'model': <class 'blacklist.models.Blacklist'>, 'action': u'post_add'}
     21>>> Record.objects.all()
     22[<Record: 127.0.0.1>, <Record: 127.0.0.2>]
     23>>> Record.objects.all()[0].blacklists.add(l)
     24{'reverse': False, 'signal': <django.db.models.signals.ModelSignal object at 0x7606ad30>, 'instance': <Record: 127.0.0.1>, 'pk_set': set([10L]), 'using': 'default', 'model': <class 'blacklist.models.Blacklist'>, 'action': u'pre_add'}
     25{'reverse': False, 'signal': <django.db.models.signals.ModelSignal object at 0x7606ad30>, 'instance': <Record: 127.0.0.1>, 'pk_set': set([10L]), 'using': 'default', 'model': <class 'blacklist.models.Blacklist'>, 'action': u'post_add'}
     26>>> Record.objects.all()
     27[<Record: 127.0.0.1>, <Record: 127.0.0.2>]
     28>>> Blacklist.objects.all()
     29[<Blacklist: test>]
     30>>> Blacklist.objects.all()[0].record_set.all()
     31[<Record: 127.0.0.1>, <Record: 127.0.0.2>]
     32>>> l = Blacklist.objects.all()[0]
     33>>> l
     34<Blacklist: test>
     35>>> l.delete()
     36>>> Record.objects.all()
     37[<Record: 127.0.0.1>, <Record: 127.0.0.2>]
     38>>> Record.objects.all()[0].blacklists.all()
     39[]
     40>>>
     41}}}
    1042
    11 Here are my models and signals with my workaround:
     43The signal receiver function:
     44{{{
     45@receiver(m2m_changed, sender=Record.blacklists.through)
     46def blacklists_changed(sender, **kwargs):
     47    print "%s" % kwargs # Only to see if any data was sent and to see if an m2m_changed signal was received.
     48
     49    if kwargs['action'] == "post_clear":
     50        if kwargs['reverse']:
     51            Record.objects.annotate(count=Count('blacklists')).exclude(count__gt=0).delete()
     52}}}
     53
     54No signal is sent at deletion of a related object when the relation was broken by deleting the object. Sorry but it isn't a duplication. Because I don't get any output that the signal function has run as when I ran the "add"-relation function and the related objects are still there without any relation to a object. If I uncomment the second function that catch a "pre_delete" and explicitly call "clear()" on the M2M-field I receive a "m2m_changed" signal and all records without a related object are deleted. According to docs "m2m_changed" was a signal that is sent when the relationship between objects are changed, which happens when the object is deleted.
     55
     56Entire code:
    1257{{{
    1358from django.db import models
     
    4792@receiver(m2m_changed, sender=Record.blacklists.through)
    4893def blacklists_changed(sender, **kwargs):
     94    print "%s" % kwargs
     95
    4996    if kwargs['action'] == "post_clear":
    5097        if kwargs['reverse']:
     
    5299
    53100
    54 @receiver(pre_delete, sender=Blacklist)
    55 def blacklist_deleted(sender, **kwargs):
    56     kwargs['instance'].record_set.clear()
     101#@receiver(pre_delete, sender=Blacklist)
     102#def blacklist_deleted(sender, **kwargs):
     103#    kwargs['instance'].record_set.clear()
    57104}}}
    58105
    59 If you comment out the blacklist_deleted and do a <Blacklist instance>.delete() no m2m_changed signal is sent at all.
    60 
    61106BR / Isaac
Back to Top