Skip to main content

Minimalistic FCM device registration for your Django app

Code style: black Codecov CircleCI

django-fcm-devices

django-fcm-devices takes a minimalistic and opinionated approach to letting your app register FCM tokens and receive push notifications.

It assumes you have an app (mobile, web, whatever!) and users who log into this app. It assumes you want to solely use FCM to send push notifications to those users. And it assumes you want the interaction between your apps and your backend to be as straight-forward as possible.

Before authoring this library, existing options were considered. They tend to provide APIs that are too expansive and brittle to allow the app code to be dead simple. They tend to make schema choices early on which are hard to tighten up later, resulting in further brittleness and race conditions.

That said, the top contenders - django-push-notifications and fcm-django - are worth checking out and just may fit your specific use-case better. Personally, I've always found myself wanting less.

This library leverages the excellent PyFCM to interact with Firebase once you've registered your devices.

This library uses semantic versioning because no one likes surprise backward incompatible changes, right?

Install

pip install django-fcm-devices

Configure

You need to configure your FCM API key by adding to your settings:

  • FCM_DEVICES_API_KEY your FCM API key.

You can optionally subclass the FCMBackend to add your own behaviour using the following setting and dot notation:

  • FCM_DEVICES_BACKEND_CLASS

Use

Manage FCM tokens

When you get your device's FCM registration token in app, you simply need to POST it to the backend along with some extra data.

POST /v1/devices/

{
    "token": "<your FCM token value>",
    "name": "A string describing your device, ie - iPhone. Just descriptive.",
    "active": true,
    "type": "ios"
}

If you get a 201 response, the token is registered and that device should now be able to receive push notifications.

Next, your user might log out of your app on their device. They will still receive push notifications despite no longer being authenticated on the app. To prevent this (if you prefer!) before logging out, simply update the device such that it is no longer active:

POST /v1/devices/

{
    "token": "<your FCM token value>",
    "name": "Jimbo's iPhone",
    "active": false,
    "type": "ios"
}

If you receive a 201 response, those changes are persisted.

Notice how similar the create and update are? In fact, they're more or less the same. This is an idempotent endpoint, which is a little unusual but not forbidden for a POST request.

Note: use of PUT for this was considered, but typically PUT is used when the caller specifies the ID of the resource in the URL. This would logically be the registration ID, however it is not clear whether or not FCM registration tokens are dependably URL-safe and I didn't want the added complexity of requiring callers to URL encode them.

Send push notifications

So you've had your app register a token - sending them a push is easy enough. This library provides some convenience functions, as follow:

from django.contrib.auth.models import User

from fcm_devices.models import Device
from fcm_devices.service import send_notification, send_notification_to_user

device = Device.objects.get(user_id=123)  # get the device for the user you want to message

send_notification(device, message_title="An important push", message_body="Oh dear ..")

# or send it straight to all the user's devices
send_notification_to_user(User.objects.get(id=123), message_title="An important push", message_body="Oh dear ..")

These functions have the added bonus of processing sending errors and deactivating devices, so they should generally be used. As kwargs, they take anything that PyFCM supports and are essentially passed through.

But what about bulk and data notifications, you ask? Well, easy does it tiger. We'll likely get there (or send along a PR?!) but in the meantime you can easily do this yourself by directly using PyFCM:

from pyfcm import FCMNotification

from fcm_devices.models import Device
from fcm_devices.settings import app_settings

push_service = FCMNotification(api_key=app_settings.API_KEY)
data_message = {
    "Nick": "Mario",
    "body": "great match!",
    "Room": "PortugalVSDenmark"
}

device = Device.objects.get(user_id=123)  # get the device for the user you want to message

# To multiple devices
result = push_service.notify_multiple_devices(
    registration_ids=[device.token],
    message_body="Hullo there",
    data_message=data_message
)

# To a single device
result = push_service.notify_single_device(
    registration_id=device.token,
    message_body="Hullo there",
    data_message=data_message
)

The life of an FCM token

The API this library exposes by default just just lets you POST device tokens. It doesn't let you list your devices or delete them yourself. This simplicity is only really OK due to the life-cycle of an FCM token.

When you get an FCM token, it is usually because you've just opened an app after installing it, restoring it or wiping its data. This token is specific to the very install of the application your using. Notably, it is not specific to the user logged into the application.

While that app installation remains intact, you can typically continue to use the token to notify that user.

Finally, when FCM attempts to deliver a message to the device and the app was uninstalled, FCM discards that message right away and invalidates the registration token. Future attempts to send a message to that device results in a NotRegistered error.

Assuming you're using the convenience methods this library provides, when a token is found to be invalid it will be marked with active set to false. This typically is the end of that token's life - you might want to periodically purge inactive tokens if you're space conscious.

Contribute

Spot a bug or something that needs a test? Contributions are very welcome, but bear in mind that this library aims to be minimalistic and somewhat opinionated in its approach and that larger feature additions should likely be put in dependent but separate apps.

Run the tests

In order to easily test on all these Pythons and run the exact same thing that CI will execute you'll want to setup pyenv and install the Python versions outlined in tox.ini.

If you are on Mac OS X, it's recommended you use brew. After installing brew run:

$ brew install pyenv pyenv-virtualenv pyenv-virtualenvwrapper

Then:

pyenv install -s 3.6.10
pyenv install -s 3.7.6
pyenv install -s 3.8.1
pyenv install -s 3.9.1
pyenv virtualenv devices 3.8.1
pyenv shell devices 3.6.10 3.7.6 3.9.1
pip install tox

And to run the test suite:

$ tox

Submit a PR

Once you've fixed your bug and added a regression test or two, feel free to submit a pull request and I'll take a look. Please be thorough in explaining what your PR aims to achieve.

Metadata

Release files for django-fcm-devices 1.0.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for django-fcm-devices 1.0.2
File Size Uploaded
django-fcm-devices-1.0.2.tar.gz 10.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for django-fcm-devices 1.0.2
File Interpreter ABI Platform
django_fcm_devices-1.0.2-py3-none-any.whl Python 3 none any Details

Total release size: 23.7 kB

Release files / django-fcm-devices-1.0.2.tar.gz

Download URL django-fcm-devices-1.0.2.tar.gz
Size 10.6 kB
Tags Source
SHA-256 checksum
How to use checksums
7edcb408a7a1ea07fdd648651360193501bb938c608c941ba2869709173d2423
BLAKE2b-256 checksum
How to use checksums
0bd9fe803bc957e904a26d1be0f26d2a712063100e6f91f3de85589850befbbf
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/3.3.0 pkginfo/1.7.0 requests/2.25.1 setuptools/52.0.0 requests-toolbelt/0.9.1 tqdm/4.56.0 CPython/3.8.7

Release files / django_fcm_devices-1.0.2-py3-none-any.whl

Download URL django_fcm_devices-1.0.2-py3-none-any.whl
Size 13.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
f2823b2686b749bf7f5aefa1577e92644179b6c7221150069e7bb830f66809eb
BLAKE2b-256 checksum
How to use checksums
080747682d147ec6e1ba8f801c20d2b59cff53e54de9114828eadc9478dbe469
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/3.3.0 pkginfo/1.7.0 requests/2.25.1 setuptools/52.0.0 requests-toolbelt/0.9.1 tqdm/4.56.0 CPython/3.8.7

Release history Release notifications | RSS feed

This release

1.0.2 This release

2 release files

1.0.1

2 release files

1.0.0

2 release files

0.0.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page