Adding Firebase Cloud Messaging Service into a Django Project

Overview

Adding Firebase Cloud Messaging Service into a Django Project

The aim of this repository is to provide a step-by-step guide and a basic project sample to implement FCM (Firebase Cloud Messaging) feature into a django-based project.

Step 1: Create a Firebase Project

Goto firebase console and click on Add project to create a new firebase project. Since the goal of using firebase is its cloud messaging feature, you can disable Google Analytics.

Step 2: Download a JSON File Including Project's Credentials

Next to the Project Overview on the left menu there is a gear icon which redirects you to the project settings page.

Click on the gear icon and select Project settings

Then, click on Service accounts and then click on Generate new private key. Download and store the JSON file on your device.

Security Note: Do NOT keep this file inside the project root and never publish it on Github, Gitlab,...

Step 3: Install the fcm-django App

You can use any other package, or you can develope a custom app yourself, but, I prefer fcm-django because it is simple and single responsible!

pip install fcm-django

For communicating through API it is necessary to install Django Rest Framework:

pip install djangorestframework

Here are the package's repository on Github and its documentation:

Step 4: Install the firebase-admin Package

The firebase-admin is the official library from firebase that communicates with its services.

pip install firebase-admin

Step 5: Modifying Django Settings

Import initialize_app from firebase_admin in your setting file:

from firebase_admin import initialize_app

Add fcm_django and rest_framework to the INSTALLED_APPS:

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    # You Apps Here...
    'rest_framework', # For Rest API
    'fcm_django', # New
]

Add required configs for fcm-django app:

# Optional ONLY IF you have initialized a firebase app already:
# Visit https://firebase.google.com/docs/admin/setup/#python
# for more options for the following:
# Store an environment variable called GOOGLE_APPLICATION_CREDENTIALS
# which is a path that point to a json file with your credentials.
# Additional arguments are available: credentials, options, name
FIREBASE_APP = initialize_app()
# To learn more, visit the docs here:
# https://cloud.google.com/docs/authentication/getting-started>

FCM_DJANGO_SETTINGS = {
     # default: _('FCM Django')
    "APP_VERBOSE_NAME": "[string for AppConfig's verbose_name]",
     # true if you want to have only one active device per registered user at a time
     # default: False
    "ONE_DEVICE_PER_USER": True/False,
     # devices to which notifications cannot be sent,
     # are deleted upon receiving error response from FCM
     # default: False
    "DELETE_INACTIVE_DEVICES": True/False,
}

Run the migrate command before running the server:

python manage.py migrate

Note: Before running the project make sure to set an environment variable named GOOGLE_APPLICATION_CREDENTIALS to the path of your downloaded JSON file from firebase in Step 2.

Step 6: The Default Service Worker

For handling background notifications, when user is not focused on tab, it is required to have a service worker. Just create a file named firebase-messaging-sw.js and put it in root, so it should be accessible by /firebase-messaging-sw.js/ URL. In django you can put this file in templates and add following line of code in your root urls.py:

from django.urls import include, path
from django.views.generic import TemplateView

urlpatterns = [
    ...,
    path("firebase-messaging-sw.js",
        TemplateView.as_view(
            template_name="firebase-messaging-sw.js",
            content_type="application/javascript",
        ),
        name="firebase-messaging-sw.js"
    ),
    ...
]

So the content of templates/firebase-messaging-sw.js is:

// [START initialize_firebase_in_sw]
// Give the service worker access to Firebase Messaging.
// Note that you can only use Firebase Messaging here, other Firebase libraries
// are not available in the service worker.
importScripts('https://www.gstatic.com/firebasejs/3.9.0/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/3.9.0/firebase-messaging.js');

// Initialize the Firebase app in the service worker by passing in the
// messagingSenderId.
firebase.initializeApp({
  // Replace messagingSenderId with yours
  'messagingSenderId': '504975596104'
});

// Retrieve an instance of Firebase Messaging so that it can handle background
// messages.
const messaging = firebase.messaging();
// [END initialize_firebase_in_sw]

// If you would like to customize notifications that are received in the
// background (Web app is closed or not in browser focus) then you should
// implement this optional method.
// [START background_handler]
messaging.setBackgroundMessageHandler(function(payload) {
  console.log('[firebase-messaging-sw.js] Received background message ', payload);
  // Customize notification here
  payload = payload.data;
  const notificationTitle = payload.title;
  const notificationOptions = {
    body: payload.body,
    icon: payload.icon_url,
  };

  self.addEventListener('notificationclick', function (event) {
    event.notification.close();
    clients.openWindow(payload.url);
  });

  return self.registration.showNotification(notificationTitle,
      notificationOptions);
});
// [END background_handler]

Step 7: Register User Devices

Registering the user device can be done by some JavaScript code which communicates with Firebase API. You can write a better, cleaner and more secure version of this code yourself. This is just for educational purposes.

">

<script src="https://www.gstatic.com/firebasejs/4.1.2/firebase.js">script>
<script>
    // Initialize Firebase
    // Firebase Console --> Settings --> General
    // --> Register App --> Copy firebaseConfig
    const firebaseConfig = {
        ...
    };


    firebase.initializeApp(firebaseConfig);

    // Firebase Messaging Service
    const messaging = firebase.messaging();
    function sendTokenToServer(currentToken) {
        if (!isTokenSentToServer()) {
            // The API Endpoint will be explained at step 8
            $.ajax({
                url: "/api/devices/",
                method: "POST",
                async: false,
                data: {
                    'registration_id': currentToken,
                    'type': 'web'
                },
                success: function (data) {
                    console.log(data);
                    setTokenSentToServer(true);
                },
                error: function (err) {
                    console.log(err);
                    setTokenSentToServer(false);
                }
            });

        } else {
            console.log('Token already sent to server so won\'t send it again ' +
                'unless it changes');
        }
    }

    function isTokenSentToServer() {
        return window.localStorage.getItem("sentToServer") === "1";
    }

    function setTokenSentToServer(sent) {
        if (sent) {
            window.localStorage.setItem("sentToServer", "1");
        } else {
            window.localStorage.setItem("sentToServer", "0");
        }
    }


    function requestPermission() {
        messaging.requestPermission().then(function () {
            console.log("Has permission!");
            resetUI();
        }).catch(function (err) {
            console.log('Unable to get permission to notify.', err);
        });
    }

    function resetUI() {
        console.log("In reset ui");
        messaging.getToken().then(function (currentToken) {
            console.log(currentToken);
            if (currentToken) {
                sendTokenToServer(currentToken);
            } else {
                setTokenSentToServer(false);
            }
        }).catch(function (err) {
            console.log(err);
            setTokenSentToServer(false);
        });
    }

    messaging.onTokenRefresh(function () {
        messaging.getToken().then(function (refreshedToken) {
            console.log("Token refreshed.");
            // Indicate that the new Instance ID token has not yet been sent to the
            // app server.
            setTokenSentToServer(false);
            // Send Instance ID token to app server.
            sendTokenToServer(refreshedToken);
            resetUI();
        }).catch(function (err) {
            console.log("Unable to retrieve refreshed token ", err);
        });
    });

    messaging.onMessage(function (payload) {
        payload = payload.data;
        // Create notification manually when user is focused on the tab
        const notificationTitle = payload.title;
        const notificationOptions = {
            body: payload.body,
            icon: payload.icon_url,
        };

        if (!("Notification" in window)) {
            console.log("This browser does not support system notifications");
        }
        // Let's check whether notification permissions have already been granted
        else if (Notification.permission === "granted") {
            // If it's okay let's create a notification
            var notification = new Notification(notificationTitle, notificationOptions);
            notification.onclick = function (event) {
                event.preventDefault(); // prevent the browser from focusing the Notification's tab
                window.open(payload.url, '_blank');
                notification.close();
            }
        }
    });


    requestPermission();
script>

Step 8: Prepare Create Device Endpoint

Simply you can add following code snippet to your root urls.py:

from fcm_django.api.rest_framework import FCMDeviceAuthorizedViewSet
from rest_framework.routers import DefaultRouter

router = DefaultRouter()
router.register('devices', FCMDeviceAuthorizedViewSet)

urlpatterns = [
    # URLs will show up at 
   
    /devices
   
    # DRF browsable API which lists all available endpoints
    path('api/', include(router.urls)),
    # ...
]

Step 9: Sending Messages

Sending message to users is quite straightforward. First, create a Message object with your customized data, then send it to target devices:

from firebase_admin.messaging import Message
from fcm_django.models import FCMDevice

message_obj = Message(
    data={
        "Nick" : "Mario",
        "body" : "great match!",
        "Room" : "PortugalVSDenmark"
   },
)

# You can still use .filter() or any methods that return QuerySet (from the chain)
device = FCMDevice.objects.all().first()
# send_message parameters include: message, dry_run, app
device.send_message(message_obj)
# Boom!

Step 10: Important Notes

  • In the sample project login and register are not the responsibility of the notifications app. I just put it there for the sake of simplicity! So, please be careful in your real-life projects!

  • Users should login at least one time in order to receive notifications.

  • These code snippets and the sample project is just a tutorial to implement FCM in django project. You should consider your use-cases and read the documentations carefully.

  • Finally, if this tutorial was helpful to you, give me a star and share it with your friends.

Owner
Seyyed Ali Ayati
I'm 22 years old, interested in Python programming language and its libraries and frameworks, also in love with software and its development process!
Seyyed Ali Ayati
Running in outer Django project folder (cd django_project)

Django Running in outer Django project folder (cd django_project) Make Migrations python manage.py makemigrations Migrate to Database python manage.py

1 Feb 07, 2022
Django Federated Login provides an authentication bridge between Django projects and OpenID-enabled identity providers.

Django Federated Login Django Federated Login provides an authentication bridge between Django projects and OpenID-enabled identity providers. The bri

Bouke Haarsma 18 Dec 29, 2020
Pipeline is an asset packaging library for Django.

Pipeline Pipeline is an asset packaging library for Django, providing both CSS and JavaScript concatenation and compression, built-in JavaScript templ

Jazzband 1.4k Jan 03, 2023
Service request portal on top of Ansible Tower

Squest - A service request portal based on Ansible Tower Squest is a Web portal that allow to expose Tower based automation as a service. If you want

Hewlett Packard Enterprise 183 Jan 04, 2023
Simple yet powerful and really extendable application for managing a blog within your Django Web site.

Django Blog Zinnia Simple yet powerful and really extendable application for managing a blog within your Django Web site. Zinnia has been made for pub

Julien Fache 2.1k Dec 24, 2022
TinyApp - A Python (Django) Full Stack Application for shortening URLs

TinyApp A Python (Django) Full Stack Application for shortening URLs. How to sta

Li Deng 1 Jan 23, 2022
wagtail_tenants is a Django/Wagtail app to provide multitenancy to your wagtail project.

wagtail-tenants wagtail_tenants is a Django/Wagtail app to provide multitenancy to your wagtail project. You are able to run a main Wagtail Site and f

<bbr> 11 Nov 20, 2022
A starter template for building a backend with Django and django-rest-framework using docker with PostgreSQL as the primary DB.

Django-Rest-Template! This is a basic starter template for a backend project with Django as the server and PostgreSQL as the database. About the templ

Akshat Sharma 11 Dec 06, 2022
Use watchfiles in Django’s autoreloader.

django-watchfiles Use watchfiles in Django’s autoreloader. Requirements Python 3.7 to 3.10 supported. Django 2.2 to 4.0 supported. Installation Instal

Adam Johnson 43 Dec 14, 2022
A django integration for huey task queue that supports multi queue management

django-huey This package is an extension of huey contrib djhuey package that allows users to manage multiple queues. Installation Using pip package ma

GAIA Software 32 Nov 26, 2022
Packs a bunch of smaller CSS files together from 1 folder.

Packs a bunch of smaller CSS files together from 1 folder.

1 Dec 09, 2021
A Django app to initialize Sentry client for your Django applications

Dj_sentry This Django application intialize Sentry SDK to your Django application. How to install You can install this packaging by using: pip install

Gandi 1 Dec 09, 2021
Website desenvolvido em Django para gerenciamento e upload de arquivos (.pdf).

Website para Gerenciamento de Arquivos Features Esta é uma aplicação full stack web construída para desenvolver habilidades com o framework Django. O

Alinne Grazielle 8 Sep 22, 2022
A set of high-level abstractions for Django forms

django-formtools Django's "formtools" is a set of high-level abstractions for Django forms. Currently for form previews and multi-step forms. This cod

Jazzband 621 Dec 30, 2022
This repository contains django library management system project.

Library Management System Django ** INSTALLATION** First of all install python on your system. Then run pip install -r requirements.txt to required se

whoisdinanath 1 Dec 26, 2022
Store events and publish to Kafka

Create an event from Django ORM object model, store the event into the database and also publish it into Kafka cluster.

Diag 6 Nov 30, 2022
Full control of form rendering in the templates.

django-floppyforms Full control of form rendering in the templates. Authors: Gregor Müllegger and many many contributors Original creator: Bruno Renié

Jazzband 811 Dec 01, 2022
Use heroicons in your Django and Jinja templates.

heroicons Use heroicons in your Django and Jinja templates. Requirements Python 3.6 to 3.9 supported. Django 2.2 to 3.2 supported. Are your tests slow

Adam Johnson 52 Dec 14, 2022
A Django app to accept payments from various payment processors via Pluggable backends.

Django-Merchant Django-Merchant is a django application that enables you to use multiple payment processors from a single API. Gateways Following gate

Agiliq 997 Dec 24, 2022
A web app which allows user to query the weather info of any place in the world

weather-app This is a web app which allows user to get the weather info of any place in the world as soon as possible. It makes use of OpenWeatherMap

Oladipo Adesiyan 3 Sep 20, 2021