How to set up user notifications for your Django app - Part 1

Introduction

Notifications provide updates about the activity on an application. In this guide, you will learn how to set up simple notifications for your Django application. This is Part 1 of this series.

When you're finished, you'll be able to set up simple notifications for your Django app.

Tutorials in this series:

Part 1 - Setting up the backend (this tutorial)

Part 2 - Frontend components with TailwindCSS and AlpineJS

Our final product will look something like this.

Simple User Notifications in Django: Navbar Dropdown

Prerequisites

To complete this tutorial, you will need:

  • Knowledge of Django
  • Django starter project
  • In this article we use TailwindCSS (styling) and AlpineJS (javascript)

LEVEL - 💻 💻 Beginner to Intermediate - Knowledge of how Django works, for example how views, URL configuration, and templates work. We will cover high-level concepts but you may need to do further reading on your own.

Key Terms

A Notification or Activity in its simplest form consists of an actor, verb, and object. It describes:

  • an action - 'user signed in'
  • performed by an actor - a user
  • on or with an object - email address, or user name  

Step 1 - Set up your project

Clone our starter repo here and follow the instructions to set up a new project.

Create a new app called 'notis' in the apps directory.

$ mkdir apps/notispython django-admin.py startapp notis ./apps/notis

Our final folder structure will look like this.

├── project_name
|   └── config ... # settings, urls.py etc.
|   └── apps
|      └── notis
├── templates
|   ├── .. # see part 2 for this
└── manage.py

Install Django Notifications

We will be using the excellent Django notifications library to do some of the heavy lifting for us.

requirements.txt

...
django-notifications-hq==1.6.0

settings.py

INSTALLED_APPS = (
    'django.contrib.auth',
    '...other apps',

    'notifications',
    
    ...
)

urls.py


from django.urls import path
import notifications.urls

urlpatterns = [
    ...
    path('inbox/notifications/', include(notifications.urls, namespace='notifications')),
    ...
]

Notes

The package will take care of:

  • Setting up a model to store our notifications. The model comes with the basic fields to store information about <actor> <verb> <action_object> <target> <time>.
  • Provides a manager with the required business logic to handle typical actions on notifications e.g. filtering, mark as read etc.
  • Signal dispatcher to simplify creating notifications.
  • Views and template tags to render notifications on your frontend Django templates or single-page app.

Using a custom model for notifications

The above should be sufficient for most apps. However, if you need to customize the notification model in order to extend the functionality you would create a new model:

apps/notis/models.py

from django.db import models
from notifications.base.models import AbstractNotification


class Notification(AbstractNotification):
    # custom field example
    category = models.ForeignKey('myapp.Category',
                                 on_delete=models.CASCADE)

    class Meta(AbstractNotification.Meta):
        abstract = False

Notes

  • created a new app called 'notis' to handle extensions to the base library. Technically the model could be added to any other app. This is more organized as it keeps your notifications logic separate from your other apps.
  • created a new model and used django's model inheritance to extend an existing model. You will note that we explicitly set abstract = False in the inner Meta class. Django actually does this for you, we have included it here to (hopefully) ignite your curiosity so you can gain a better understanding of how django's model inheritance works.

settings.py


..
NOTIFICATIONS_NOTIFICATION_MODEL = 'apps.notis.Notification'

Notes

  • inform the django-notifications library that we will be using another model for Notifications.

Create notifications based on user events

Let's go through an example of how we can use this on a real application to store user sign-in and sign-out events and display them to the user.

A user story is an informal, natural language description of features of a software system (Wikipedia). An example user story for this would be, "as a user, I want to be able to see my sign-in activity".

Breaking it down into actionable tasks

  • a) Listen for a user login or logout actions and store them on the database
  • b) Create frontend component to display notifications (Part 2)
  • c) Connect our frontend component to the backend. (Part 2)

User login or logout events

We need to find a way to get notified when certain actions occur. Fortunately, we don't have to look too far, Django provides a signal dispatcher for just this purpose. You can read more about it here.

Additionally, the Django auth framework already provides prebuilt signals for login and logout actions that our code can listen to.

Setup the signal receivers

apps/users/signals.py


from django.contrib.auth import user_logged_in, user_logged_out
from notifications.signals import notify


@receiver(user_logged_in)
def user_signed_in(request, user, **kwargs):    
  notify.send(user, recipient=user, verb=_("You signed in"))

@receiver(user_logged_in)
def user_signed_out(request, user, **kwargs):    
  notify.send(user, recipient=user, verb=_("You signed out"))

Notes

  • We created two receiver functions and connected them to the user_logged_in & user_logged_out signals using the @receiver decorator.
  • We added business logic to create a notification instance whenever the signal is received. notify.send is a signal provided by the Django-notifications library. The actor recipient is the user.
  • Behind the scenes, the Django-notifications library will create a notification instance when the signal is dispatched.

Connect the signal receivers

notis/apps.py

class NotisConfig(AppConfig):
    name = "apps.notis"

    def ready(self):
        try:
            import apps.notis.signals 
        except ImportError:
            pass

Notes:

  • Signal receivers are connected in the ready() method of your application configuration class. This process happens after the app registration process. See the docs for a more technical explanation.

Conclusion

In this tutorial, we learned how to set up the backend to handle notifications for our Django app. In the next part, we will create frontend components and connect them to the backend.

Further reading

  • Activity Streams - Definitions for the terms used in describing a Notification/Activity

Copyright © 2022 www.advantch.com