Django m2m_changed signal is never called
Asked Answered
M

1

15

I can't understand why my m2m_changed signal is not triggered.

Here is the code:

models.py

class Badge(TimeStampable, Expirable, Deactivable,
            SafeDeleteModel):
    _safedelete_policy = HARD_DELETE

    owner = models.ForeignKey(settings.AUTH_USER_MODEL,
                              blank=True, null=True,
                              on_delete=models.PROTECT)
    restaurants = models.ManyToManyField(Restaurant)
    identifier = models.CharField(max_length=2048)

    objects = SafeDeleteManager.from_queryset(BadgeQuerySet)()

signals.py

from django.db.models.signals import m2m_changed
from django.dispatch import receiver

from .models import Badge

@receiver(m2m_changed, sender=Badge.restaurants.through)
def my_callback(sender, **kwargs):
    print("M2M has been changed!")

apps.py (this post advised to change this file)

from django.apps import AppConfig

class BadgesConfig(AppConfig):
    name = 'badges'

    def ready(self):
        import badges.signals

When going to a shell:

  • m2m_changed.receivers returns an empty list (it does not work, and the signal can never be received)

I found similar post but did not found the answer in it.

Why m2m_changed signal does not work?

EDIT

When opening a shell and importing badges.signals, it works. It means the problem is in apps.py:

In [1]: from django.db.models.signals import m2m_changed

In [2]: m2m_changed.receivers
Out[2]: []

In [3]: import badges.signals

In [4]: m2m_changed.receivers
Out[4]:
[((4551224720, 4520068792),
  <weakref at 0x10f4da5e8; to 'function' at 0x10f462d90 (check_uniqueness)>)]
Maui answered 31/5, 2018 at 13:25 Comment(16)
Please do from django.db.models.signals import m2m_changed and post (in your question) what this gives you: m2m_changed.receivers (if it isn't an empty list, in which case the signal was not connected).Anabas
@Anabas It is an empty list!Maui
Try adding m2m_changed.connect(my_callback) to the end of signals.py and check m2m_changed.receivers again. (Might have to remove the receiver decorator from your callback).Anabas
The list is still empty :(Maui
Try importing badges.signals in your app's __init__.py as mentioned here: click. (Either the init.py in the folder with settings.py or your app's main folder should work)Anabas
Seems it's not related to how signals are import because it works for another type of signal (see my updated question).Maui
How are you trying to trigger m2m_changed in the shell?Gnathic
@BenjaminHicks yesMaui
No, I mean what code are you using to make the m2m trigger, i.e. like badge_obj.restaurants.add(rest_obj) or similar?Gnathic
Yes, exactly this.Maui
Out of curiosity which version of Django are you using?Pascal
@JacquesGaudin 1.11.13Maui
Just giving my 2p's worth: have you tried @receiver(m2m_changed, sender=Badge.restaurants.through, weak=False)?Pascal
can you show us your __init__.py in this django app?Dariodariole
@JacquesGaudin I tried with weak=False but nothing changed. @SardorbekImomaliev __init__.py is empty, but that's expected because I use apps.py to register the signal, as advised in documentation. (I already tried with the init method and did not work)Maui
When opening a shell and importing badges.signals, it works. It means the problem is in apps.py!! (question edited).Maui
M
8

I found my mistake in this section of Django the documentation. From the beggining, my apps configurations were never called!

Actually, to register an app correctly, I have two methods:

method 1

INSTALLED_APPS = ['badges', ...]

and declaring in __init__.py: default_app_config = 'badges.apps.BadgesConfig'

method 2

INSTALLED_APPS = ['badges.apps.BadgesConfig']

my mistake

I was using the INSTALLED_APPS = ['badges', ...] without declaring anything in __init__.py

I think maybe Django could display a warning when it notices that apps.py is in a folder app but never used.

Maui answered 4/6, 2018 at 10:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.