Home » Django » the right place to keep my signals.py files in django

the right place to keep my signals.py files in django

Posted by: admin November 30, 2017 Leave a comment

Questions:

based on the documentation i was reading from django site, It seems like signals.py in the app folder is a good place to start with, but the problem am facing is that when I create signals for pre_save and I try to import the class from model, it conflicts with the import operation for task in model. see my code below

model.py

from django.contrib.auth.models import User
from django.db import models
from django.utils.translation import gettext as _
from signals import *
class Comm_Queue(CommunicatorAbstract):
    queue_statuses = (
        ('P', _('Pending')),
        ('S', _('Sent')),
        ('E', _('Error')),
        ('R', _('Rejected')),
    )
    status          = models.CharField(max_length=10, db_index=True, default='P')
    is_html         = models.BooleanField(default=False)
    language        = models.CharField(max_length=6, choices=settings.LANGUAGES)
    sender_email    = models.EmailField()
    recipient_email = models.EmailField()
    subject         = models.CharField(max_length=100)
    content         = models.TextField()

signals.py

from django.conf import settings
from django.db.models.signals import pre_save
from django.dispatch import receiver
from models import Comm_Queue


@receiver(pre_save, sender=Comm_Queue)
def get_sender_email_from_settings(sender, **kwargs):
    obj=kwargs['instance']
    if not obj.sender_email:
        obj.sender_email='%s' % settings.ADMINS[0][1]

this code will not run because am import Comm_queue inside signals and am doing * import to signals inside models.py. can any one advice how I could over come this issue?

regards,

Answers:

Original answer, for Django < 1.7:

You can register the signals by importing signals.py in the app’s __init__.py file:

# __init__.py
import signals

This will allow to import models.py from signals.py without circular import errors.

One problem with this approach is that it messes up the coverage results if you’re using coverage.py.

Related discussion

Edit: For Django >= 1.7:

Since AppConfig was introduced, the recommended way of importing signals is in it’s init() function. See Eric Marcos’ answer for more details.

Questions:
Answers:

If you’re using Django<=1.6 I’d recommend Kamagatos solution: just import your signals at the end of your models module.

For future versions of Django (>=1.7), the recommended way is to import your signals module in your app’s config ready() function:

my_app/apps.py

from django.apps import AppConfig

class MyAppConfig(AppConfig):
    name = 'my_app'

    def ready(self):
        import my_app.signals

my_app/__init__.py

default_app_config = 'my_app.apps.MyAppConfig'

Questions:
Answers:

To solve your problem you just have to import signals.py after your model definition. That’s all.

Questions:
Answers:

I also put signals in signals.py file and also have this code snippet that loads all signals:

# import this in url.py file !

import logging

from importlib import import_module

from django.conf import settings

logger = logging.getLogger(__name__)

signal_modules = {}

for app in settings.INSTALLED_APPS:
    signals_module = '%s.signals' % app
    try:
        logger.debug('loading "%s" ..' % signals_module)
        signal_modules[app] = import_module(signals_module)
    except ImportError as e:
        logger.warning(
            'failed to import "%s", reason: %s' % (signals_module, str(e)))

This is for project, I’m not sure if it works at app level.

Questions:
Answers:

I’m guessing that you’re doing that so your signals are registered, so that they’re found somewhere. I just put my signals right in a models.py file normally.

Questions:
Answers:

In old Django versions would be fine to put the signals on the __init__.py or maybe in the models.py(although at the end models will be way to large for my taste).

With Django 1.9, it is better I think, to place the signals on a signals.py file and import them with the apps.py, where they are going to be loaded after loading the model.

apps.py:

from django.apps import AppConfig


class PollsConfig(AppConfig):
    name = 'polls'

    def ready(self):
        from . import signals  # NOQA

You can also divide your signals on signals.py and handlers.py in another folder within your model named signals as well, but for me that is just over engineering. Take a look at Placing Signals

Questions:
Answers:

An alternative is to import the callback functions from signals.py and connect them in models.py:

signals.py

def pre_save_callback_function(sender, instance, **kwargs):
    # Do stuff here

model.py

# Your imports here
from django.db.models.signals import pre_save
from yourapp.signals import pre_save_callback_function

class YourModel:
    # Model stuff here
pre_save.connect(pre_save_callback_function, sender=YourModel)

Ps: Importing YourModel in signals.py will create a recursion; use sender, instead.

Ps2: Saving the instance again in the callback function will create a recursion. You can make a control argument in .save method to control it.