How Django Signals Work

Stand-alone Project to show how-to #PureDjango — Episode #00

J3
Jungletronics
7 min readApr 6, 2023

--

This is a standalone Django Working App Running a simple Project of How:

Signals Works in Django.

Welcome!
In Django, signals allow certain senders 
to inform a set of receivers
that specific actions have occurred.

Django signals are used to send and
receive specific essential information
whenever a data model is
saved, changed, or even removed.TE

In Django, signals are a way for different parts of the application to communicate with each other in a loosely coupled manner. A signal is essentially a message sent by one part of the application (the sender) to another part (the receiver) to notify it of some event that has occurred.

Signals in Django work using a publisher-subscriber pattern. The publisher (sender) sends out a signal, and one or more subscribers (receivers) listen for that signal and respond accordingly.

Let’s make a Django Signals project now.

00#Step — Let´s Make the Django Infrastructure:

Open your prompt and type (or copy/paste/hit enter):

mkdir django_signals
cd django_signals
python -m venv ENV
source ENV/bin/activate
pip install django
code .
exit()

source /home/j3/django_signals/ENV/bin/activate
Run command above to activate virtual box (ENV) inside MS VSCode

Now create the Project and the App by typing:

django-admin startproject signal_project
python manage.py startapp signal_app
This scaffolding dirs that will be created by Django machinery! 🤗️

01#Step — Open signal_project/settings.py and type:

INSTALLED_APPS = [
...
'signal_app.apps.SignalAppConfig',
]

In Django, registering an app in the settings.py file tells Django to include that app in the project and makes its functionality available to the project.

When you create a new Django project, the settings.py file is automatically created in the root directory of the project. This file contains various settings that configure how the project behaves. One of these settings is the INSTALLED_APPS list, which specifies the names of all the Django apps that are included in the project.

To register a new app in the settings.py file, you simply add the name of the app to the INSTALLED_APPS list. For example, if you have an app named myapp, you would add the following line to the INSTALLED_APPS list:

myapp.apps.MyAppConfig

Once you’ve added the app to the INSTALLED_APPS list, Django will automatically discover the app's models, templates, static files, and other resources and make them available to the project.

Registering an app in the settings.py file also enables you to use Django's built-in tools, such as the admin interface, to interact with the app's models and other components. It also allows other apps within the project to depend on the registered app and use its functionality.

In summary, registering an app in the settings.py file is an important step in integrating the app with the rest of the Django project and making its functionality available to the project.

02#Step — Open signal_app/models.py and type:

#signal_project/signal_app/models.py

from django.db import models
from django.contrib.auth.models import User


class Profile(models.Model):

user = models.OneToOneField(
User, on_delete=models.CASCADE, blank=True, null=True)
first_name = models.CharField(max_length=200, blank=True, null=True)
last_name = models.CharField(max_length=200, blank=True, null=True)
phone = models.CharField(max_length=200, blank=True, null=True)

def __str__(self):
return str(self.user)
We are creating ONE-TO-ONE relationship between User — Profile entities.

03#Step —Now Go to signal_app/admin.py:

#signal_project/signal_app/admin.py

from django.contrib import admin
from signal_app.models import Profile

admin.site.register(Profile)

This will register your app to admin dashboard.

04#Step — Inside signal_app/models.py paste these snippet code:

#signal_project/signal_app/models.py

from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver

class Profile(models.Model):
user =
...

def __str__(self):
...

def create_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
print('****************')
print('Profile created!')
print('****************')

post_save.connect(create_profile, sender=User)

def update_profile(sender, instance, created, **kwargs):
if created is False:
instance.profile.save()
print('****************')
print('Profile Updated!')
print('****************')

post_save.connect(update_profile, sender=User)

Django provides several built-in signals that can be used to trigger specific actions in response to certain events. For example, the pre_save and post_save signals are sent before and after an object is saved to the database, respectively. Similarly, the pre_delete and post_delete signals are sent before and after an object is deleted from the database.

To use signals in Django, you first define the signal (i.e., the message) that you want to send. This is typically done by creating an instance of the django.dispatch.Signal class. You can then connect one or more receivers to the signal using the signal.connect() method. When the signal is sent, all connected receivers are called with the sender and any additional data that was provided.

Above’s an example of how signals can be used in Django ☝️

In this example, we define a signal that is triggered after an instance of User is saved to the database. We then connect a receiver function (create and update_profile) to the signal using the post_save.connect() method.

When the _post_save signal is sent, the create_profile() function will be called with the sender (User) and any additional data that was provided.

To see all we logged a message to the python console the respective message as soon as it returns a newly created instance of the User class represented by the instance object.

Overall, signals provide a powerful way to decouple different parts of a Django application and enable them to communicate with each other in a flexible and extensible way.

05#Step — Now it’s time to migrate everything! In Terminal, type:

python manage.py makemigrations

makemigrations is used to create new database migrations based on changes to your models.

A migration is a Python file that contains the changes needed to update the database schema.

When you make changes to your models, you should create a new migration using the makemigrations command.

For example, if you add a new field to a model or change the name of a field, you would create a new migration.

Here’s an example of how to create a new migration:

Creating a new migration

In this example, when you run this command, Django will analyze your models and generate a new migration file in the myapp/migrations/ directory.

06#Step — Now migrate:

python manage.py migrate
Here’s an example of how to apply a migration

Once you have created a new migration, you can apply it to the database using the migrate command. This command updates the database schema to match the current state of your models.

07#Step — Now the last step until here, create a super user:

python manage.py createsuperuser
As soon your user instance is created, the sender message your receiver and a Profile object is created instantly!

08#Step — Run the server:

python manage.py runserver

09#Step —Now update j3 profile by changing User fields:

The update message was sent and printed in the console. All safe and sound! 🥰️

10#Step — Now let’s make in the Django way:

Create a new file named signal_app/signals.py

Get the trigger message from signal_app/models.py and paste it inside signals.py.

#signal_project/signal_app/signals.py

from django.contrib.auth.models import User
from signal_app.models import Profile
from django.db.models.signals import post_save
from django.dispatch import receiver


@receiver(post_save, sender=User)
def create_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
print('****************')
print('Profile created!')
print('****************')


# post_save.connect(create_profile, sender=User)

@receiver(post_save, sender=User)
def update_profile(sender, instance, created, **kwargs):
if created is False:
instance.profile.save()
print('****************')
print('Profile Updated!')
print('****************')


# post_save.connect(update_profile, sender=User)

In this example, we define a signal that is triggered before an instance of User is saved to the database. We then connect a receiver function (create & update) to the signal using the @receiver decorator. When the post_save signal is sent, the create_profile() function will be called with the sender (User) and any additional data that was provided (in case we just log console).

Overall, signals provide a powerful way to decouple different parts of a Django application and enable them to communicate with each other in a flexible and extensible way.

Here is the db created:

Here is ER Diagram! Enjoy it! Note One-To_One relationship between User-Profile

12#Step —Create a new User and Update its email to test:

Test it by running:

python manage.py runserver
The decorator woks fine!

Using decorators in Django is an important and common practice that can help you to modify the behavior of functions and class-based views in a flexible and modular way.

Decorators in Python are functions that modify the behavior of other functions or classes. In Django, decorators are often used to implement features like authentication, caching, rate limiting, and more. They can also be used to define custom middle-ware or to add additional functionality to existing views.

For example, the @login_required decorator in Django is used to ensure that a user is authenticated before allowing access to a particular view. The @cache_page decorator can be used to cache the output of a view for a specified amount of time, reducing the load on the server and improving performance.

Here’s an example of how to use the @login_required decorator:

from django.contrib.auth.decorators import login_required
from django.shortcuts import render

@login_required
def my_view(request):
# Do something
return render(request, 'my_template.html', {})

In this example, the my_view function is decorated with @login_required. This means that before the view is executed, Django will check whether the user is authenticated. If the user is not authenticated, they will be redirected to the login page.

Overall, using decorators in Django can help you to write cleaner, more modular code by separating concerns and allowing you to easily add or remove functionality as needed.

That’s all folks!

Hope this helps!

Bye!

👉️github

--

--

J3
Jungletronics

Hi, Guys o/ I am J3! I am just a hobby-dev, playing around with Python, Django, Ruby, Rails, Lego, Arduino, Raspy, PIC, AI… Welcome! Join us!