Django + DjangoCMS signal handlers not being called

519 views Asked by At

So, in Django 1.7.7 a new way to handle signals was introduced. I'm using 1.7.7 with django_cms, running on Python 2.

I'm trying to implement this new way, and even though the documentation is scarce but straightforward enough, it just won't work. I think Django-CMS or one of its plugins has something to do with it.

What I'm trying to do is increase the counter of my CounterModel by 1 for each MyModel that is saved on the pre_save signal of the MyModel model.

I've concluded it just doesn't work because a raise Exception('it runs') in the increase_counter function does not get raised..

I have the following:

myapp/models/mymodel.py

from .counter_model import CounterModel
# Imports here

class MyModel(models.Model):
    name = models.CharField(max_length=128)
    categories = models.ManyToManyField(CounterModel)

myapp/models/counter_model.py

# Imports here

class CounterModel(models.Model):
    amount_of_mymodels = models.PositiveIntegerField(default=0)

myapp/signals.py

from .models.mymodel import MyModel
# Other imports here

@receiver(pre_save, sender=MyModel)
def increase_counter(sender, **kwargs):
    instance = kwargs.get('instance')
    for category in instance.categories.all():
        category.amount_of_mymodels += 1

myapp/apps.py

from django.apps import AppConfig
# Other imports here

class MyAppConfig(AppConfig):
    name = "myapp"

    def ready(self):
        import myapp.signals

myapp/__init__.py

default_app_config = 'myapp.apps.MyAppConfig'

The import signals in my apps.py is executed, because when i raise an Exception there it's raised in my console (in the ready() function).

Hope someone can clarify this issue i'm having!

BTW: I've also added myapp to my INSTALLED_APPS

UPDATE: I've tried the new signal approach in another project (Python 3, also Django 1.7) and it works fine. If anyone has any clue as to what could be causing the failure of signals in my other project please let me know! I'm going to try to debug this for now, every form of help is appreciated.

NOTE: For everyone thinking 'the for loop might be empty, print something', please note the following in the beginning of my question: I've concluded it just doesn't work because a raise Exception('it runs') in the increase_counter function does not get raised... Thanks!

2

There are 2 answers

1
markwalker_ On BEST ANSWER

In all my CMS projects I've resorted to a simple import signals as the last line in my models.py because attempting the import anywhere else seems to cause issues. I don't like this approach, but I've never had any issues with signals being received & as of yet not found a better solution. Most of my projects where this is the case are on late CMS 3 builds with django 1.6 and 1.7.

This question is a little hard to follow given it's progression, so if I've missed anything, please comment.

3
xyres On

Fix 1

I tested your code by putting the signals.py code into models.py and it works.

Why's that?

From Django docs:

Where should this code live?

Strictly speaking, signal handling and registration code can live anywhere you like, although it's recommended to avoid the application's root module and its models module to minimize side-effects of importing code.

In practice, signal handlers are usually defined in a signals submodule of the application they relate to. Signal receivers are connected in the ready() method of your application configuration class. If you're using the receiver() decorator, simply import the signals submodule inside ready().

Fix 2

If you don't want to put signals.py code in your models.py, you can do the following

class MyAppConfig(...):
   ...

    def ready(self):
        # you've to import your app's signals.py 
        # not Django's signals module
        import myapp.signals

EDIT

There are some awesome answers and alternatives on this question: the right place to keep my signals.py files in django