Django app for Firebase Cloud Messaging. Used as an unified platform for sending push notifications to mobile devices & browsers (android / ios / chrome / firefox / ...).
Supports Firebase Cloud Messaging HTTP v1 API. If you're looking for the legacy API, use fcm-django<1
!
- FCMDevice model fields
- registration_id (required - is FCM token)
- name (optional)
- active (default: true)
- user (optional)
- device_id (optional - can be used to uniquely identify devices)
- type ('android', 'web', 'ios')
- Functionality:
- all necessary migrations
- model admins for django admin
- admin actions for testing single and bulk notification sending
- automatic device pruning: devices to which notifications fail to send are marked as inactive
- devices marked as inactive will not be sent notifications
- Django rest framework viewsets
Unsure how to use this project? Check out the demo at: https://github.com/xtrinch/fcm-django-web-demo
We've replaced Python package pyfcm
for Firebase's own package firebase-admin
.
Thus, we no longer use an API key. Instead, you'll need an environment variable
GOOGLE_APPLICATION_CREDENTIALS
which is a path pointing to your JSON-file stored
credentials. To learn more or view other options to input credentials, visit the
Google Cloud docs.
Finally, in your settings.py
(or whatever imported file), add:
from firebase_admin import initialize_app
FIREBASE_APP = initialize_app()
# Or just
initialize_app()
The API for sending messages is now under the firebase-admin
package; hence,
we removed the methods send_data_message
from the QuerySet and class instance
methods. Instead, everything is under a single method: send_message
from firebase_admin.messaging import Message, Notification
FCMDevice.objects.send_message(Message(data=dict()))
# Note: You can also combine the data and notification kwarg
FCMDevice.objects.send_message(
Message(notification=Notification(title="title", body="body", image="image_url"))
)
device = FCMDevice.objects.first()
device.send_message(Message(...))
Additionally, we've added Firebase's new Topic API, allowing for easier sending of bulk messages.
from firebase_admin.messaging import Message, Notification
topic = "A topic"
FCMDevice.objects.handle_subscription(True, topic)
message = Message(..., topic=topic)
FCMDevice.objects.filter(is_cool=True).send_message(message)
There are two additional parameters to both methods:
skip_registration_id_lookup
and additional_registration_ids
.
Visit Sending Messages to learn more.
Note: registration_ids
is actually incorrect terminology as it
should actually be called registration tokens
. However, to be
consistent with django-push-notifications
, we've refrained from
switching to stay backwards compatible in the docs and with the
sister package.
You can install the library directly from pypi using pip:
pip install fcm-django
Edit your settings.py file:
from firebase_admin import initialize_app
INSTALLED_APPS = (
...
"fcm_django"
...
)
# 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 = {
# an instance of firebase_admin.App to be used as default for all fcm-django requests
# default: None (the default Firebase app)
"DEFAULT_FIREBASE_APP": None,
# 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,
# Transform create of an existing Device (based on registration id) into
# an update. See the section
# "Update of device with duplicate registration ID" for more details.
# default: False
"UPDATE_ON_DUPLICATE_REG_ID": True/False,
}
Native Django migrations are in use. manage.py migrate
will install and migrate all models.
You can read more about different types of messages here.
In short, there are two types: notifications and data messages.
Notification:
from firebase_admin.messaging import Message, Notification
Message(
notification=Notification(title="title", body="text", image="url"),
topic="Optional topic parameter: Whatever you want",
)
Data message:
from firebase_admin.messaging import Message
Message(
data={
"Nick" : "Mario",
"body" : "great match!",
"Room" : "PortugalVSDenmark"
},
topic="Optional topic parameter: Whatever you want",
)
As in the following example, you can send either a notification, a data message, or both.
You can also customize the Android, iOS, and Web configuration along with additional
FCM conditions. Visit firebase_admin.messaging.Message
to learn more about those
configurations.
Additional parameters are additional_registration_ids
and
skip_registration_id_lookup
. View the "Additional Parameters"
section for more information.
from firebase_admin.messaging import Message
from fcm_django.models import FCMDevice
# 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(data={...}))
from firebase_admin.messaging import Message
from fcm_django.models import FCMDevice
# You can still use .filter() or any methods that return QuerySet (from the chain)
devices = FCMDevice.objects.all()
devices.send_message(Message(data={...}))
# Or (send_message parameters include: messages, dry_run, app)
FCMDevice.objects.send_message(Message(...))
from fcm_django.models import FCMDevice
# Subscribing
FCMDevice.objects.all().handle_topic_subscription(True, topic="TOPIC NAME"))
device = FCMDevice.objects.all().first()
device.handle_topic_subscription(True, topic="TOPIC NAME"))
# Finally you can send a message to that topic
from firebase_admin.messaging import Message
message = Message(..., topic="A topic")
# You can still use .filter() or any methods that return QuerySet (from the chain)
FCMDevice.objects.send_message(message)
# Unsubscribing
FCMDevice.objects.all().handle_topic_subscription(False, topic="TOPIC NAME"))
device = FCMDevice.objects.all().first()
device.handle_topic_subscription(False, topic="TOPIC NAME"))
from fcm_django.models import FCMDevice
FCMDevice.send_topic_message(Message(data={...}), "TOPIC NAME")
You can add additional_registration_ids (Sequence) for manually sending registration IDs. It will append these IDs to the queryset lookup's returned registration IDs.
You can also add skip_registration_id_lookup (bool) to skip database lookup that goes along with your query.
from firebase_admin.messaging import Message
from fcm_django.models import FCMDevice
FCMDevice.objects.send_message(Message(...), False, ["registration_ids"])
By default the message will be sent using the default FCM firebase_admin.App
(we initialized this in our settings),
or the one specified with the DEFAULT_FIREBASE_APP
setting.
This default can be overridden by specifying an app when calling send_message. This can be used to send messages using different firebase projects.
from firebase_app import App
from firebase_app.messaging import Notification
from fcm_django.models import FCMDevice
device = FCMDevice.objects.all().first()
device.send_message(notification=Notification(...), app=App(...))
If you want to use a specific Firebase app for all fcm-django requests, you can create an instance of
firebase_admin.App
and pass it to fcm-django with the DEFAULT_FIREBASE_APP
setting.
The DEFAULT_FIREBASE_APP
will be used for all send / subscribe / unsubscribe requests, include FCMDevice
's
admin actions.
In your settings.py
:
from firebase_admin import initialize_app, credentials
from google.auth import load_credentials_from_file
from google.oauth2.service_account import Credentials
# create a custom Credentials class to load a non-default google service account JSON
class CustomFirebaseCredentials(credentials.ApplicationDefault):
def __init__(self, account_file_path: str):
super().__init__()
self._account_file_path = account_file_path
def _load_credential(self):
if not self._g_credential:
self._g_credential, self._project_id = load_credentials_from_file(self._account_file_path,
scopes=credentials._scopes)
# init default firebase app
# this loads the default google service account with GOOGLE_APPLICATION_CREDENTIALS env variable
FIREBASE_APP = initialize_app()
# init second firebase app for fcm-django
# the environment variable contains a path to the custom google service account JSON
custom_credentials = CustomFirebaseCredentials(os.getenv('CUSTOM_GOOGLE_APPLICATION_CREDENTIALS'))
FIREBASE_MESSAGING_APP = initialize_app(custom_credentials, name='messaging')
FCM_DJANGO_SETTINGS = {
"DEFAULT_FIREBASE_APP": FIREBASE_MESSAGING_APP,
# [...] your other settings
}
Viewsets come in two different varieties:
FCMDeviceViewSet
- Permissions as specified in settings (
AllowAny
by default, which is not recommended) - A device may be registered without associating it with a user
- Will not allow duplicate registration_id's
- Permissions as specified in settings (
FCMDeviceAuthorizedViewSet
- Permissions are
IsAuthenticated
and custom permissionIsOwner
, which will only allow therequest.user
to get and update devices that belong to that user - Requires a user to be authenticated, so all devices will be associated with a user
- Will allow duplicate registration_id's for different users, so you are responsible for cleanup (if you do not want duplicate registration id's, use the
UPDATE_ON_DUPLICATE_REG_ID
flag)
- Permissions are
Routes can be added one of two ways:
- Routers (include all views)
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 <api_root>/devices
# DRF browsable API which lists all available endpoints
path('', include(router.urls)),
# ...
]
- Using as_view (specify which views to include)
from fcm_django.api.rest_framework import FCMDeviceAuthorizedViewSet
urlpatterns = [
# Only allow creation of devices by authenticated users
path('devices', FCMDeviceAuthorizedViewSet.as_view({'post': 'create'}), name='create_fcm_device'),
# ...
]
The DRF viewset enforce the uniqueness of the registration ID. In some use case it may cause an issue: If an already registered mobile device changes its user, then it will fail to register because the registration ID already exist.
When option UPDATE_ON_DUPLICATE_REG_ID
is set to True, then any creation of
device with an already existing registration ID will be transformed into an update.
The UPDATE_ON_DUPLICATE_REG_ID
only works with DRF.
fcm-django
is fully compatible with Python 3.6+
Compatible with Django versions 3.0+.
For Django version 2.2, use version fcm-django < 2
.
For lower django versions, use version fcm-django < 1
.
Library relies on firebase-admin-sdk for sending notifications, for more info about all the possible fields, see: https://github.com/firebase/firebase-admin-python
Migration from v0 to v1 was done by Andrew-Chen-Wang
Submit an issue/PR on this project. Please do not send me emails, as then the community has no chance to see your questions / provide answers.
To setup the development environment, simply do pip install -r requirements.txt
To manually run the pre-commit hook, run pre-commit run --all-files.