Django Signals
I've been working on a website for some time now, at first I've made it with Django + templates and now I use Django Rest Framework and Angular. The website is supposed to be a platform for uploading papers and offer the ability to peer review them.
What I want to talk about in this short article is how I simplified the development using Django signals for certain use cases.
The official docs describe signals as:
Django includes a “signal dispatcher” which helps allow 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. They’re especially useful when many pieces of code may be interested in the same events.
In my website I needed to use signals in the following situations:
Create a profile for every user.
Update model when something changed.
Create a profile for every user.
I have a profile model already defined, let's ignore it.
[code language="python"] @receiver(post_save, sender=settings.AUTH_USER_MODEL) def create_profile(sender, instance, created, **kwargs): """ Ensure that every new user gets a profile. """ if created: Profile.objects.create(user=instance) [/code]
That was the signal that creates a new profile for every user that is created, pretty easy, right? I will not even bother explaining the code. At first I was overriding the create function from a custom serialiser, but that only worked when I was creating users via API calls, It didn't work when the admin created new users, overriding save method of the model was cumbersome and I avoided it.
Update model when something changed.
Here's the code for the other two signals:
[code language="python"] @receiver(pre_save, sender=Paper) def editor_field_changed(sender, instance, **kwargs): """ If there's an editor and paper status is processing. Change the status to under_review. If there's no editor and the paper status is under_review, change the status to processing. """ if instance.editor and instance.status == Paper.STATUS_CHOICES[0][0]: instance.status = Paper.STATUS_CHOICES[1][0] # under_review elif instance.editor == None and instance.status == Paper.STATUS_CHOICES[1][0]: instance.status = Paper.STATUS_CHOICES[0][0] # processing [/code]
Basically, what this does is to set the paper status to under_review when an editor is assigned to it and the paper's current status is processing. Notice that the signal is transmitted at the pre_save event by Paper model.
[code language="python"] @receiver(post_save, sender=Review) def reviews_changed(sender, instance, **kwargs): """ If there's an editor review, set the paper status according to the result of the editor review. """ if instance.editor_review: # If the editor review is positive, change the paper status to published. if instance.appropriate == Review.APPROPRIATE_CHOICES[0][0] \ and instance.recommendation == Review.RECOMMENDATION_CHOICES[1][0]: instance.paper.status = Paper.STATUS_CHOICES[3][0] # accepted else: instance.paper.status = Paper.STATUS_CHOICES[2][0] # preliminary_reject instance.paper.save() [/code]
This signal is transmitted at post_save by the Review model, it sets the paper status to accepted or preliminary_reject. The review instance has a paper object so the models are tied together.
Thank you, I hope this helped you! If you have any questions feel free to comment below or ask me on twitter.
Resources
https://docs.djangoproject.com/en/2.0/topics/signals/
https://docs.djangoproject.com/en/2.0/ref/signals/#