Welcome to django-notifs!¶
Modular Notifications (InApp, Email, SMS, CustomBackend etc) for Django

django-notifs is a modular notifications app for Django that basically allows you to notify users about events that occur in your application E.g
Your profile has been verified
User xxxx sent you a message
It also allows you to deliver these notifications to any destination you want to with custom delivery channels.
It also supports asynchronous notification with several pluggable delivery backends (e.g Celery, RQ etc)
Examples?¶
A tutorial on how to build a Realtime Chat application with Vue, django-notifs, RabbitMQ and uWSGI
The Repository for the chat app (Chatire) is also available on github
Documentation¶
Contents¶
Overview¶
Requirements¶
Python 3.6+
Django 2.2+
Supported Functionality¶
In-app notifications
Silent notifications (i.e Notifications that aren’t saved in the database)
Delivery providers e.g Email, Slack, SMS etc
Custom delivery channels and providers
Asynchronous notifications (with support for multiple backends e.g Celery, RQ, AwsLambda etc)
Supported providers¶
Email: SMTP and Transaction email providers (Amazon SES, Mailgun, Mailjet, Postmark, SendinBlue, SendGrid, SparkPost and Mandrill) with django-anymail
SMS: (Twilio, Messagebird) with django-sms
Installation¶
Get it from pip with:
pip install django-notifs
Include it in settings.INSTALLED_APPS
:
INSTALLED_APPS = (
'django.contrib.auth',
...
'notifications',
'django_jsonfield_backport' # if you're running django < 3.1
...
)
Finally don’t forget to run the migrations with:
python manage.py migrate notifications
You can also register the current Notification model in django admin:
"""admin.py file."""
from django.contrib import admin
from notifications.utils import get_notification_model
Notification = get_notification_model()
admin.site.register(Notification)
Usage¶
Quick start¶
To Create/Send a notification import the notify function and call it with the following arguments:
from notifications.utils import notify
notify(
**notification_kwargs, # Notification kwargs that map to the current Notification model
silent=True, # Don't persist to the database
countdown=0 # delay (in seconds) before sending the notification
channels=('email', 'slack'),
extra_data={
'context': {} # Context for the specified Notification channels
}
)
This example creates a silent notification and delivers it via email
and slack
.
This assumes that you’ve implemented these channels
A NotificationChannel is a class thats builds a payload from a Notification object and sends it to one or more providers. Below is an example of a channel that builds a payload containing the context and provider and then, delivers it to the inbuilt Console provider (which simply prints out any payload that it receives):
from notifications.channels import BaseNotificationChannel
class CustomNotificationChannel(BaseNotificationChannel):
name = 'custom_notification_channel'
providers = ['console']
def build_payload(self, provider):
return {'context': self.context, 'payload': provider}
Note
The build_payload
method accepts the current provider as an argument so you can return a different payload
based on the current provider.
Then you can instantiate the Notification channel directly:
console_notification = ConsoleNotificationChannel(
notification: Notification, context={'arbitrary_data': 'data'}
)
console_notification.notify() # Send immediately
console_notification.notify(countdown=60) # Send after 1 minute
This gives you more flexibility over the notify
utility function because you can create several notifications
and decide how each individual notification should be sent
Note
Notification channels are automatically registered by django-notifs
You must inherit from the base class and specify the name
property for the channel to be properly registered
Bulk sending¶
You can send bulk notifications by setting the bulk
property to True
in the context dictionary:
console_notification = ConsoleNotificationChannel(
notification: Notification, context={'bulk': True, 'arbitrary_data': 'data'}
)
console_notification.notify()
or:
notify(
...,
extra_data={
'context': {
'bulk': True,
}
}
)
Note
The provider takes care of sending the payload in the most efficient way.
(Some providers like pusher_channels
have a bulk api for delivering multiple notifications in a single batch).
Notification Model¶
Django notifs includes an inbuilt notification model with the following fields:
source: A ForeignKey to Django’s User model (optional if it’s not a User to User Notification).
source_display_name: A User Friendly name for the source of the notification.
recipient: The Recipient of the notification. It’s a ForeignKey to Django’s User model.
category: Arbitrary category that can be used to group messages.
action: Verbal action for the notification E.g Sent, Cancelled, Bought e.t.c
obj: An arbitrary object associated with the notification using the `contenttypes` app (optional).
short_description: The body of the notification.
url: The url of the object associated with the notification (optional).
silent: If this Value is set, the notification won’t be persisted to the database.
extra_data: Arbitrary data as in a JSONField.
channels: Notification channels related to the notification (Tuple/List in a JSONField)
The values of the fields can easily be used to construct the notification message.
Extra/Arbitrary Data¶
Besides the standard fields, django-notifs allows you to attach arbitrary data (as JSON) to a notification. Simply pass in a dictionary as the extra_data argument.
Note
This field is only persisted to the database if you use use the default Notification model or a custom model
that provides an extra_data
field.
Sending notifications asynchronously¶
django-notifs
is designed to support different backends for delivering notifications.
By default it uses the Synchronous
backend which delivers notifications synchronously.
Note
The Synchronous backend is not suitable for production because it blocks the request. It’s more suitable for testing and debugging. To deliver notification asynchronously, please see the backends section.
Delayed notifications¶
You can delay a notification by passing the countdown
(in seconds) parameter to the notify
function:
# delay notification for one minute
notify(**kwargs, countdown=60)
Reading notifications¶
To read a notification use the read method:
from notifications.utils import read
# id of the notification object, you can easily pass this through a URL
notify_id = request.GET.get('notify_id')
# Read notification
if notify_id:
read(notify_id=notify_id, recipient=request.user)
Note
It’s really important to pass the correct recipient to the read
function.
Internally,it’s used to check if the user has the right to read the notification.
If you pass in the wrong recipient or you omit it entirely, django-notifs
will raise a
NotificationError
Configuration¶
NOTIFICATIONS_MODEL
¶
Default='notifications.models.Notification'
This setting is used override the default database Model for saving notifications. Most users wouldn’t need to override this but it can be useful if you’re trying to integrate django-notifs into an existing project that already has it’s own Notificaiton model
NOTIFICATIONS_DELIVERY_BACKEND
¶
Default='notifications.backends.Synchronous
django-notifs
is designed to support different backends for delivering notifications.
By default it uses the Synchronous
backend which delivers notifications synchronously.
Note
The Synchronous backend is not suitable for production because it blocks the request. It’s more suitable for testing and debugging. To deliver notification asynchronously, please see the backends section.
NOTIFICATIONS_QUEUE_NAME
¶
Default='django_notifs'
This setting is only valid for the Celery, Channels and RQ backend
This is the queue name for backends that have a “queue” functionality
NOTIFICATIONS_RETRY
¶
Default=False
Enable the retry functionality.
The Retry functionality is only valid for the Celery and RQ backends
Backends¶
The primary function of a delivery backend is to execute the code of the delivery channels and providers. Unlike notification channels, you can only use one delivery backend at a time.
Celery¶
Install the optional Celery dependency with:
pip install django-notifs[celery]
Enable it by setting NOTIFICATIONS_DELIVERY_BACKEND
to notifications.backends.Celery
Run celery with the command:
celery -A yourapp worker -l info -Q django-notifs
Whenever a notification is created, it’s automatically sent to celery and processed.
Make sure you see the queue and task (notifications.backends.celery.consume
) in the terminal.

If you have issues registering the task, you can import it manually or checkout the Celery settings in the repo.
Channels¶
Install the channels dependency with:
pip install django-notifs[channels]
This also installs channels_redis as an extra dependency
Declare the notifications consumer in asgi.py
:
from notifications import consumers
application = ProtocolTypeRouter({
...,
'channel': ChannelNameRouter({
'django_notifs': consumers.DjangoNotifsConsumer.as_asgi(),
})
})
This example assumes that you’re running Django 3x Which has native support for asgi. Check the channels documentation for Django 2.2
Next add the django_notifs channel layer to settings.CHANNEL_LAYERS
:
CHANNEL_LAYERS = {
...,
'django_notifs': {
'BACKEND': 'channels_redis.core.RedisChannelLayer',
'CONFIG': {
"hosts": [('127.0.0.1', 6379)],
},
},
}
Finally, run the worker with:
python manage.py runworker django_notifs

RQ¶
RQ is a lightweight alternative to Celery. To use the RQ Backend, install the optional dependency with:
pip install django-notifs[rq]
django notifs uses django-rq under the hood
Enable it by setting NOTIFICATIONS_DELIVERY_BACKEND
to notifications.backends.RQ
Configure the django_notifs
in settings.py
:
RQ_QUEUES = {
...,
'django_notifs': {
'HOST': 'localhost',
'PORT': 6379,
'DB': 0,
'PASSWORD': '',
'DEFAULT_TIMEOUT': 360,
}
}
Finally start the rq worker with:
python manage.py rqworker django_notifs --with-scheduler

See the django-rq documentation for more details
AwsLambda (with SQS)¶
The setup for this backend is more involved but it’s probably the cheapest and most scalable backend to use in production because the heavylifting and execution environment is handled by AWS.
set NOTIFICATIONS_DELIVERY_BACKEND
to notifications.backends.AwsSqsLambda
This backend uses boto3
under the hood; so make sure your AWS credentials are configured e.g:
export AWS_ACCESS_KEY_ID=xxxx
export AWS_SECRET_ACCESS_KEY=xxxx
export AWS_DEFAULT_REGION=xxxx
Clone the lambda worker repository and run:
npm install
The sqs-lambda-worker
folder includes four files that are of interest:
.env.example
You can use this file (after renaming it to .env
) to configure the environment variables for the autogenerated Lambda function.
You can replace this step by:
Configuring the environment variables in your CI/CD environment (Recommended)
Exporting them in the current shell.
This is useful if you want to test the serverless deployment locally before moving it to your CI/CD
requirements.txt
In order to keep the lambda function as lean as possible, you have to explicitly declare the requirements that are necessary for the lambda function. New providers (and their dependencies) are continuously added to django-notifs so it’s not adviseable to install dependencies for providers that you don’t need because this could impact the startup time of your Lambda function.
serverless.yml
The Serverless file. It contains a blueprint that deploys the simplest configuration possible but the configuration options are endless. see the Serverless documentation for AWS for more information.
settings.py
Declare the Django settings for the lambda function.
After setting these variables deploy the serverless stack to AWS:
serverless deploy --stage <your-stage>
Then update your settings with the generated sqs queue url:
settings.NOTIFICATIONS_SQS_QUEUE_URL = 'xxxxxx' # autogenerated SQS url
Synchronous¶
This is the default backend that sends notifications synchronously.
You can enable it explicitly by setting NOTIFICATIONS_DELIVERY_BACKEND
to notifications.backends.Synchronous
Providers¶
Django notifs comes with a set of inbuilt providers. These providers are typically classes that accept a payload and contain the logic for delivering the payload to an external service.
Below are the list of supported providers:
Email¶
name: 'email'
The email provider uses the standard django.core.mail
module.
This opens up support for multiple ESP’s (Mailjet, Mailchimp, sendgrid etc)
Settings¶
If you use django-anymail
or a custom Email backend, all you have to do configure the settings and dependencies as you’d
normally do and the email provider should pick it up.
Payload¶
Single:
{
'subject': 'The subject line of the email',
'body': 'The body text. This should be a plain text message',
'from_email': 'The sender’s address',
'to': 'A list or tuple of recipient addresses',
'bcc': 'A list or tuple of addresses used in the “Bcc” header when sending the email',
'attachments': 'A list of attachments to put on the message',
'headers': 'A dictionary of extra headers to put on the message'.
'cc': 'A list or tuple of recipient addresses used in the “Cc” header when sending the email',
'reply_to': 'A list or tuple of recipient addresses used in the “Reply-To” header when sending the email',
**extra_esp,
}
extra_esp
is any extra data that you want to pass to your custom Email backend.
SMS (with django-sms)¶
name: 'django_sms'
The SMS provider uses a third-party app called django-sms this also opens up support for multiple SMS providers.
Supported providers are:
Twilio
Message bird
Installation¶
pip install django-notifs[sms]
Extra dependencies can be installed by:
pip install django-sms[twilio,messagebird]
Settings¶
See the django-sms documentation for more information on how to configure your preferred backend. Once it is configured,
django-notifs
should pick it up
Payload¶
Single:
{
'body': 'Sample message',
'originator': '+10000000000',
'recipients': ['+20000000000', '+30000000000'] # list of recipients
}
Slack¶
name: 'slack'
Installation¶
pip install django-notifs[slack]
Settings¶
NOTIFICATIONS_SLACK_BOT_TOKEN=xxxxxxx
Pusher Channels¶
name: 'pusher_channels'
Installation¶
pip install django-notifs[pusher_channels]
Settings¶
NOTIFICATIONS_PUSHER_CHANNELS_URL=https://<app_id>:<app_secret>@api-eu.pusher.com/apps/0000000
FCM (Firebase Web push)¶
name: 'fcm_web'
Settings¶
NOTIFICATIONS_FCM_KEY=xxxxxxx
Payload¶
Single:
{
'title': 'notification title',
'body': 'body',
'click_action': 'https://example.com',
'icon': 'icon,
'to': 'user_token',
}
django-channels¶
name: 'django_channels'
Installation¶
pip install django-notifs[channels]
Settings¶
NOTIFICATIONS_WEBSOCKET_EVENT_NAME
¶
Default='notifs_websocket_message'
The type
value of the messages that are going to received by the django notifs websocket consumer.
In most cases, you don’t need to change this setting.
NOTIFICATIONS_WEBSOCKET_URL_PARAM
¶
Default = 'room_name'
The WebSocket URL param name. It’s also used to construct the WebSocket URL. See the Advanced usage section for more information.
Context¶
{
'destination': 'Group/channel name'
}
Payload¶
Single:
{
'type': settings.NOTIFICATIONS_WEBSOCKET_EVENT_NAME, # or a custom event name
'message': {},
}
Writing custom Providers¶
Sometimes, the inbuilt providers are not sufficient to handle every use case.
You can create a custom provider by inheriting from the Base provider class or an existing Provider and Implementing the
send
and send_bulk
method.
The Notification context is also available as a property (self.context
):
from notifications.providers import BaseNotificationProvider
class CustomNotificationProvider(BaseNotificationProvider):
name = 'custom_provider'
def send(self, payload):
# call an external API?
pass
def send_bulk(self, payloads):
for payload in payloads:
self.send(payload)
# or call an external bulk API?
Advanced usage¶
Tentative Notifications¶
A tentative notification is a conditional notification that should only be sent if a criteria is met.
An example is sending a notification if a user hasn’t read a chat message in 30 minutes (as a reminder).
You can acheive this by combining the countdown
functionality with a custom provider:
# delay notification for 30 minutes
notify(**kwargs, countdown=1800)
Custom provider:
from notifications.utils import get_notification_model
from notifications.providers import BaseNotificationProvider
class DelayedNotificationProvider(BaseNotificationProvider):
name = 'delayed_notifier'
def send(self, payload):
notification_id = self.payload['notification_id']
notification = get_notification_model().objects.get(id=self.notification_id)
if notification.read:
return
# send the notification
In this example, we abort the notification if the notification has been read when the provider is executed.
WebSockets¶
Unlike other django notification libraries that provide an API for accessing notifications, django-notifs supports websockets out of the box (thanks to django-channels). This makes it easy to send realtime notifications to your users in reaction to a new server side event.
If you’re unfamiliar with django-channels. It’s advised to go through the documentation so you can understand the basics.
Setting up the WebSocket server¶
This section assumes that you’ve already installed django-channels
Setup the consumer routing in your asgi.py
file:
import os
import django
from django.core.asgi import get_asgi_application
from channels.routing import ProtocolTypeRouter, URLRouter
from notifications import routing as notifications_routing
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'yourapp.settings')
application = ProtocolTypeRouter({
'http': get_asgi_application(),
'websocket': URLRouter(notifications_routing.websocket_urlpatterns)
})
Notification channels¶
A simple WebSocket channel is provided:
notifications.channels.DjangoWebSocketChannel
Sample usage:
notif_args = {
...
extra_data: {
'context': {
'channel_layer': 'default',
'destination': 'group or channel_name',
'message': {'text': 'Hello world'}
}
}
}
notify(**notif_args, channels=['websocket'])
Running the WebSocket server¶
ASGI
is capable of handling regular HTTP and WebSocket traffic so you don’t really need to run a dedicated
WebSocket server but it’s still an option.
see the channels deployment documentation for more information on the best way to deploy your application.
How to listen to notifications¶
You listen to notifications by connecting to the WebSocket URL.
The default URL is http://localhost:8000/<settings.NOTIFICATIONS_WEBSOCKET_URL_PARAM>
To connect to a WebSocket room (via JavaScript) for a user john_doe
you’ll need to connect to:
var websocket = new WebSocket('ws://localhost:8000/john_doe')
You can always change the default route by Importing the notifications.consumers.DjangoNotifsWebsocketConsumer
consumer and declaring another route. If you decide to do that, make sure you use the
NOTIFICATIONS_WEBSOCKET_URL_PARAM
setting because the Consumer class relies on it
an example to prefix the URL with /chat
would be:
from django.urls import path
from . import default_settings as settings
from .consumers import DjangoNotifsWebsocketConsumer
websocket_urlpatterns = [
path(
f'chat/<{settings.NOTIFICATIONS_WEBSOCKET_URL_PARAM}>',
DjangoNotifsWebsocketConsumer.as_asgi()
)
]
Authentication?¶
This is out of the scope of django-notifs for now. This might change in the future as django-channels becomes more mature. Hence, The WebSocket endpoint is unprotected and you’ll probably want to roll out your own custom authentication backend if you don’t make use of the standard Authentication backend.
Testing and Debugging¶
django-notifs comes with an inbuilt 'console'
provider that just prints out the notification payload:
class MyNotificationChannel:
providers = ['console']
...
This can be helpful during development when it’s used with the Synchronous backend.