Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it approproate it use django signals within the same app

Tags:

python

django

Trying to add email notification to my app in the cleanest way possible. When certain fields of a model change, app should send a notification to a user. Here's my old solution:

from django.contrib.auth import User

class MyModel(models.Model):
    user = models.ForeignKey(User)
    field_a = models.CharField()
    field_b = models.CharField()

    def save(self, *args, **kwargs):
        old = self.__class__.objects.get(pk=self.pk) if self.pk else None
        super(MyModel, self).save(*args, **kwargs)
        if old and old.field_b != self.field_b:
            self.notify("b-changed")
        # Sevelar more events here
        # ...

    def notify(self, event)
        subj, text = self._prepare_notification(event)
        send_mail(subj, body, settings.DEFAULT_FROM_EMAIL, [self.user.email], fail_silently=True)

This worked fine while I had one or two notification types, but after that just felt wrong to have so much code in my save() method. So, I changed code to signal-based:

from django.db.models import signals

def remember_old(sender, instance, **kwargs):
    """pre_save hanlder to save clean copy of original record into `old` attribute
    """
    instance.old = None
    if instance.pk:
        try:
            instance.old = sender.objects.get(pk=instance.pk)
        except ObjectDoesNotExist:
            pass

def on_mymodel_save(sender, instance, created, **kwargs):
    old = instance.old
    if old and old.field_b != instance.field_b:
        self.notify("b-changed")
    # Sevelar more events here
    # ...

signals.pre_save.connect(remember_old, sender=MyModel, dispatch_uid="mymodel-remember-old")
signals.post_save.connect(on_mymodel_save, sender=MyModel, dispatch_uid="mymodel-on-save")

The benefit is that I can separate event handlers into different module, reducing size of models.py and I can enable/disable them individually. The downside is that this solution is more code and signal handlers are separated from model itself and unknowing reader can miss them altogether. So, colleagues, do you think it's worth it?

like image 805
Alexander Lebedev Avatar asked Apr 16 '10 18:04

Alexander Lebedev


People also ask

Should we use Django signals?

The only reason to use signalsOnly use signals to avoid introducing circular dependencies. If you have two apps, and one app wants to trigger behaviour in an app it already knows about, don't use signals. The app should just import the function it needs and call it directly.

What is the use of Django signals?

Django includes a “signal dispatcher” which helps decoupled applications get notified when actions occur elsewhere in the framework. In a nutshell, signals allow certain senders to notify a set of receivers that some action has taken place.

Are Django signals synchronous?

First, to dispel a misconception about signals, they are not executed asynchronously. There is no background thread or worker to execute them. Like most of Django, they are fully "synchronous".

How many types of signals are there in Django?

There are 3 types of signal. pre_save/post_save: This signal works before/after the method save(). pre_delete/post_delete: This signal works before after delete a model's instance (method delete()) this signal is thrown. pre_init/post_init: This signal is thrown before/after instantiating a model (__init__() method).


2 Answers

I think it's a good idea. The "Custom Signals for Uncoupled Design" talk from the most recent DjangoCon is a great resource of what is possible and appropriate with signals in Django.

like image 149
Ludwik Trammer Avatar answered Nov 15 '22 04:11

Ludwik Trammer


I think using signals here is a good design decision. The notification isn't part of the save, it's a consequence of the save. Dealing with these types of consequences is the reason Django provides signals.

like image 35
Zach Avatar answered Nov 15 '22 03:11

Zach