djangodjango-signals

Django signals for different apps


I need to have separate signals for each app of my project. But after saving a model in one of the apps signals trigger from all apps, so I have duplicated messages from my signals. What is the right way to set up signals for different apps of Django project?

signals.py of my app1:

# -*- coding: utf-8 -*-
import logging

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

from .models import *


@receiver(post_save)
def log_app1_updated_added_event(sender, **kwargs):
    '''Writes information about newly added or updated objects of app1 into log file'''

    logger = logging.getLogger(__name__)

    app1_object = kwargs['instance']
    
    if kwargs['created']:
        logger.info(f'added {app1_object }')
    else:
        logger.info(f'updated {app1_object }')

signals.py of my app2:

# -*- coding: utf-8 -*-
import logging

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

from .models import *


@receiver(post_save)
def log_app2_updated_added_event(sender, **kwargs):
    '''Writes information about newly added or updated objects of app2 into log file'''

    logger = logging.getLogger(__name__)

    app2_object = kwargs['instance']
    
    if kwargs['created']:
        logger.info(f'added {app2_object }')
    else:
        logger.info(f'updated {app2_object }')

apps.py of my app1:

from django.apps import AppConfig


class App1Config(AppConfig):
    name = 'App1'

    def ready(self):
        from app1 import signals

apps.py of my app2:

from django.apps import AppConfig


class App1Config(AppConfig):
    name = 'App2'

    def ready(self):
        from app2 import signals

and here is section of loggers in my project settings.py

'loggers': {
        'django': {
            'handlers': ['console',],
            'propagate': False,
            'level': 'INFO',
        },
        'app1.signals': {
            'handlers': ['console', 'file'],
            'level': 'INFO',
        },
        'app2.signals': {
            'handlers': ['console', 'file'],
            'level': 'INFO',
        },
     }

Solution

  • Well that makes perfect sense: apps are not that much functionally isolated, it just makes it easy to export models and logic in a separate app, then plug these into another project, and thus makes code more reusable.

    But if you make a signal that listens to post_save signals, it will be triggered when saving models of any app. The signal handler does not limit listening to models of its own app. That would also limit the signals too much: it would for example make it impossible to listen to changes by the User model, since that is defined in the django.contrib.auth app.

    But we can filter it to only let signals process only the model objects of their own app with:

    @receiver(post_save)
    def log_app1_updated_added_event(sender, instance, **kwargs):
        'Writes information about newly added or updated objects of app1 into log file'
        if sender._meta.app_label == 'app1':
            logger = logging.getLogger(__name__)
    
            app1_object = instance
    
            if kwargs['created']:
                logger.info(f'added {app1_object }')
            else:
                logger.info(f'updated {app1_object }')

    we thus check if the model has an app_label that is 'app1' and if not, we just ignore it.