FCM Django
fcm-django provides a convenient way to integrate Firebase Cloud Messaging (FCM) into Django applications, allowing developers to send push notifications to mobile devices and web browsers. It simplifies device registration and message dispatching. The current version is 3.2.0, and the library maintains an active development cycle with frequent releases addressing new features and compatibility updates.
Common errors
-
ModuleNotFoundError: No module named 'fcm_django'
cause The `fcm-django` package is not installed or not available in the current Python environment.fixRun `pip install fcm-django` to install the package. -
django.core.exceptions.ImproperlyConfigured: FCM_DJANGO_SETTINGS must be configured.
cause The `FCM_DJANGO_SETTINGS` dictionary is missing or incorrectly defined in your Django project's `settings.py`.fixAdd a valid `FCM_DJANGO_SETTINGS` dictionary to your `settings.py`, containing the Firebase Admin SDK credentials, e.g., `FCM_DJANGO_SETTINGS = {"FIREBASE_ADMIN_CREDENTIALS": {...}}`. -
django.db.utils.ProgrammingError: (1071, 'Specified key was too long; max key length is 767 bytes')
cause This error occurs with MySQL databases when a unique index on the `registration_id` (a TEXT field) exceeds MySQL's default key length limit.fixAdd `FCM_DJANGO_SETTINGS = {'MYSQL_COMPATIBILITY': True}` to your `settings.py` before running Django migrations. This setting removes the unique constraint from the problematic field. -
RuntimeError: The app default app has not been initialized. Make sure to call initialize_app() before using any of the Firebase services.
cause The Firebase Admin SDK has not been correctly initialized. While `fcm-django` usually handles this internally upon first access, it can indicate a problem with `FCM_DJANGO_SETTINGS` or an unusual import order.fixVerify that `fcm_django` is included in `INSTALLED_APPS` and that `FCM_DJANGO_SETTINGS` is correctly configured in your `settings.py`.
Warnings
- breaking Python 3.9 support was dropped in `fcm-django` 3.0.0. The minimum supported Python version is now 3.10.
- breaking Python 3.6 support was dropped in `fcm-django` 2.0.0. Older Python versions must use `fcm-django <2.0.0`.
- deprecated The underlying `firebase-admin` library deprecated `send_all()` in favor of `send_each()` for sending messages to multiple tokens. `fcm-django` versions prior to 2.1.0 might still use the deprecated method, which could lead to warnings or unexpected behavior with newer `firebase-admin` versions.
- gotcha For MySQL databases, unique constraints on `registration_id` (a TextField) can exceed the maximum key length (767 bytes) for default `ROW_FORMAT` settings. This can cause migration failures.
- gotcha `FCM_DJANGO_SETTINGS` in `settings.py` must be correctly configured with valid Firebase Admin SDK credentials (e.g., `FIREBASE_ADMIN_CREDENTIALS` or `FIREBASE_ADMIN_CREDENTIALS_FILE`). Misconfiguration or missing settings will prevent `fcm-django` from initializing Firebase and will lead to runtime errors when attempting to send messages.
Install
-
pip install fcm-django
Imports
- FCMDevice
from fcm_django.models import FCMDevice
- Message, Notification
from firebase_admin.messaging import Message, Notification
Quickstart
import os
from django.conf import settings
from fcm_django.models import FCMDevice
from firebase_admin.messaging import Message, Notification
# --- Configure Django settings (this would typically be in your settings.py) ---
# For a runnable example, we configure it on the fly.
# Replace with your actual Firebase project credentials or use environment variables.
# Set environment variables for actual testing, e.g., in your shell:
# export FCM_PROJECT_ID="your-firebase-project-id"
# export FCM_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n"
# export FCM_CLIENT_EMAIL="firebase-adminsdk@your-project-id.iam.gserviceaccount.com"
# export FCM_TEST_DEVICE_TOKEN="your_actual_device_token_from_client_app"
if not settings.configured:
settings.configure(
INSTALLED_APPS=[
'django.contrib.auth', # Required for user foreign key in FCMDevice
'django.contrib.contenttypes',
'fcm_django'
],
DATABASES={'default': {'ENGINE': 'django.db.backends.sqlite3', 'NAME': ':memory:'}},
DEBUG=True,
SECRET_KEY='a-very-secret-key-for-example', # Placeholder for runnable example
FCM_DJANGO_SETTINGS={
"FIREBASE_ADMIN_CREDENTIALS": {
"type": "service_account",
"project_id": os.environ.get("FCM_PROJECT_ID", "your-firebase-project-id"),
"private_key_id": os.environ.get("FCM_PRIVATE_KEY_ID", "your-private-key-id"),
"private_key": os.environ.get("FCM_PRIVATE_KEY", "-----BEGIN PRIVATE KEY-----\nPLACE_YOUR_PRIVATE_KEY_HERE\n-----END PRIVATE KEY-----\n").replace("\\n", "\n"),
"client_email": os.environ.get("FCM_CLIENT_EMAIL", "firebase-adminsdk@your-project-id.iam.gserviceaccount.com"),
"client_id": os.environ.get("FCM_CLIENT_ID", "12345678901234567890"),
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk%40your-project-id.iam.gserviceaccount.com"
}
# Alternatively, specify a path to your service account JSON file:
# "FIREBASE_ADMIN_CREDENTIALS_FILE": os.environ.get("FCM_CREDENTIALS_PATH", None)
}
)
# Initialize Django apps system and create tables for fcm_django model
import django
django.setup()
from django.db import connection
with connection.schema_editor() as schema_editor:
try:
schema_editor.create_model(FCMDevice)
except Exception as e:
# Ignore if table already exists, common in re-runs of snippet
if 'already exists' not in str(e):
raise
# --- Your application logic (e.g., in a view, management command, or script) ---
# This token would come from your client-side Firebase SDK.
# Replace with a real FCM device token for actual testing.
device_token = os.environ.get("FCM_TEST_DEVICE_TOKEN", "YOUR_FCM_DEVICE_TOKEN_HERE")
user = None # Link to a Django User model instance if available
if "YOUR_FCM_DEVICE_TOKEN_HERE" in device_token:
print("Warning: Please replace 'YOUR_FCM_DEVICE_TOKEN_HERE' with an actual device token from your client-side app.")
print(" Without a valid token, no notification will be sent.")
# Register or get an FCM device
device, created = FCMDevice.objects.get_or_create(
registration_id=device_token,
defaults={
"name": "Test Device",
"active": True,
"type": "android" # or 'ios', 'web'
}
)
if created:
print(f"Registered new FCM device: {device.registration_id}")
else:
print(f"FCM device already registered or updated: {device.registration_id}")
# Send a notification to the device
try:
# Using FCMDeviceQuerySet.send_message for convenience and bulk support
response = FCMDevice.objects.filter(id=device.id).send_message(
title="Hello from FCM-Django!",
body="This is a test push notification sent via FCM.",
data={"custom_key": "custom_value"}, # Optional data payload
sound="default" # Optional sound
)
print(f"Notification sent successfully: {response}")
except Exception as e:
print(f"Error sending notification: {e}")
if "InvalidRegistration" in str(e) or "NotFoundError" in str(e):
print("This often means the device token is invalid or expired. Ensure 'YOUR_FCM_DEVICE_TOKEN_HERE' is current.")
elif "default app has not been initialized" in str(e):
print("Firebase Admin SDK not initialized. Check FCM_DJANGO_SETTINGS configuration.")