fcm-django:Django Firebase Cloud Messaging 推送通知统一平台

FCM Django: Send push notifications via django to websites, iOS & android mobile devices through FCM (Firebase Cloud Messaging)

分支7Tags63
文件最后提交记录最后更新时间
2 个月前
4 年前
2 个月前
2 个月前
2 个月前
1 年前
2 个月前
11 个月前
2 个月前
9 年前
9 年前
2 个月前
3 年前
2 个月前
2 个月前
2 个月前
3 年前
2 个月前

fcm-django

.. image:: https://badge.fury.io/py/fcm-django.svg :target: https://badge.fury.io/py/fcm-django

适用于 Firebase Cloud Messaging 的 Django 应用。用作向移动设备和浏览器(安卓 / iOS / Chrome / Firefox / ...)发送推送通知的统一平台。

支持 Firebase Cloud Messaging HTTP v1 API。如果您需要旧版 API,请使用 fcm-django<1

异步查询集发送 API 需要 firebase-admin>=6.9,因为它们直接使用 firebase_admin.messaging.send_each_async

FCMDevice 模型字段

  • registration_id(必填 - 即 FCM 令牌)
  • name(可选)
  • active(默认值:true)
  • user(可选)
  • device_id(可选 - 可用于唯一标识设备)
  • type('android'、'web'、'ios')

功能:

  • 所有必要的迁移
  • Django 管理后台的模型管理界面
  • 用于测试单个和批量通知发送的管理操作
  • 自动设备清理:向其发送通知失败的设备会被标记为非活动状态
  • 标记为非活动状态的设备将不会收到通知
  • Django REST framework 视图集

演示 JavaScript 客户端项目

不确定如何使用此项目?请查看以下演示: https://github.com/xtrinch/fcm-django-web-demo

从 1.0 之前版本升级

如果您仍在从基于 pyfcm 的旧版本迁移,请参阅 v1.0 迁移指南

设置

您可以使用 pip 直接从 PyPI 安装此库:

.. code-block:: console

pip install fcm-django

编辑您的 settings.py 文件:

.. code-block:: python

from firebase_admin import initialize_app

INSTALLED_APPS = (
    ...
    "fcm_django"
    ...
)

# 仅当您已初始化 Firebase 应用时才是可选的:
# 访问 https://firebase.google.com/docs/admin/setup/#python
# 了解以下内容的更多选项:
# 存储一个名为 GOOGLE_APPLICATION_CREDENTIALS 的环境变量,
# 该变量是指向包含您凭据的 json 文件的路径。
# 还可以使用其他参数:credentials、options、name
FIREBASE_APP = initialize_app()
# 要了解更多信息,请访问此处的文档:
# https://cloud.google.com/docs/authentication/getting-started>

FCM_DJANGO_SETTINGS = {
     # 一个 firebase_admin.App 实例,用作所有 fcm-django 请求的默认应用
     # 默认值:None(默认的 Firebase 应用)
    "DEFAULT_FIREBASE_APP": None,
     # 默认值:_('FCM Django')
    "APP_VERBOSE_NAME": "[AppConfig 的 verbose_name 字符串]",
     # 如果您希望每个已注册用户一次只有一个活动设备,则设为 true
     # 默认值:False
    "ONE_DEVICE_PER_USER": True/False,
     # 无法向其发送通知的设备,
     # 在从 FCM 收到错误响应后会被删除
     # 默认值:False
    "DELETE_INACTIVE_DEVICES": True/False,
     # 当此库停用设备时发出 ``device_deactivated`` 信号
     # 默认值:False
    "EMIT_DEVICE_DEACTIVATED_SIGNAL": True/False,
}

使用原生 Django 迁移。执行 manage.py migrate 将安装并迁移所有模型。

消息

您可以在此处_了解更多关于不同类型消息的信息。

.. _here: https://firebase.google.com/docs/cloud-messaging/concept-options

简而言之,有两种类型:通知消息和数据消息。

通知消息:

.. code-block:: python

from firebase_admin.messaging import Message, Notification Message( notification=Notification(title="title", body="text", image="url"), topic="Optional topic parameter: Whatever you want", )

数据消息:

.. code-block:: python

from firebase_admin.messaging import Message Message( data={ "Nick" : "Mario", "body" : "great match!", "Room" : "PortugalVSDenmark" }, topic="Optional topic parameter: Whatever you want", )

如以下示例所示,您可以发送通知消息、数据消息,或同时发送两者。您还可以自定义 Android、iOS 和 Web 配置以及其他 FCM 条件。访问 firebase_admin.messaging.Message 以了解有关这些配置的更多信息。

发送消息

其他参数包括 additional_registration_idsskip_registration_id_lookup。查看“其他参数”部分了解更多信息。

.. code-block:: python

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

# 您仍然可以使用 .filter() 或任何返回 QuerySet 的方法(来自链式调用)
device = FCMDevice.objects.all().first()
# send_message 参数包括:message, dry_run, app
device.send_message(Message(data={...}))
device.send_message(Message(data={...}), dry_run=True)

批量发送消息

.. code-block:: python

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

# 您仍然可以使用 .filter() 或任何返回 QuerySet 的方法(来自链式调用)
devices = FCMDevice.objects.all()
devices.send_message(Message(data={...}))
devices.send_message(Message(data={...}), dry_run=True)
# 或者(send_message 参数包括:message, dry_run, app)
FCMDevice.objects.send_message(Message(...))

使用 dry_run=True 可以与 Firebase 验证负载,而不会实际发送通知。这在开发和集成测试期间非常有用,您可以验证消息构造,而无需向设备发送真实的推送通知。

发送消息时会引发 firebase-admin 引发的所有错误,因此请确保在应用程序代码中捕获并处理这些错误:

  • FirebaseError – 当向 FCM 服务发送消息时发生错误。
  • ValueError – 如果输入参数无效。

有关更多信息,请参阅 https://firebase.google.com/docs/reference/admin/python/firebase_admin.messaging#firebase_admin.messaging.BatchResponse

检查批量发送失败

send_message() 会返回一个围绕 Firebase 的 BatchResponse 封装的 FirebaseResponseDict 对象。对于批量发送,Firebase 可能会在响应中返回每个设备的失败信息,而不是对整个调用抛出异常。

返回对象上的有用字段包括:

  • response.success_count
  • response.failure_count
  • response.has_failures
  • response.all_failed
  • response.failed_registration_ids
  • response.failed_exceptions
  • response.summary

示例:

.. code-block:: python

response = FCMDevice.objects.send_message(Message(...))

if response.has_failures:
    print(response.failure_count)
    print(response.failed_registration_ids)
    print(response.failed_exceptions)

这对于配置相关的失败(例如 APNS 或凭据问题)特别有用,在这些情况下,设备可能会失败但不会被停用。

设备停用信号

如果您希望在 fcm-django 停用设备时有一个明确的钩子,请启用以下设置:

.. code-block:: python

FCM_DJANGO_SETTINGS = { "EMIT_DEVICE_DEACTIVATED_SIGNAL": True, }

然后订阅 device_deactivated

.. code-block:: python

from fcm_django.signals import device_deactivated

def on_device_deactivated(
    sender,
    registration_ids,
    device_ids,
    user_ids,
    reason,
    source,
    metadata,
    **kwargs,
):
    print(registration_ids)
    print(device_ids)
    print(user_ids)
    print(reason)
    print(source)
    print(metadata)

device_deactivated.connect(on_device_deactivated)

该信号默认是禁用的,当库管理的设备停用时会触发。其负载包括:

  • registration_ids:被停用的注册令牌
  • device_ids:匹配的设备主键
  • user_ids:匹配的用户主键,不包括没有用户的设备
  • reason:停用原因
  • source:触发停用的库调用位置
  • metadata:额外上下文,例如 failed_exceptions

当前的 reason 值包括:

  • firebase_error
  • one_device_per_user
  • duplicate_registration_id
  • manual_disable

当前的 source 值包括:

  • send_message
  • perform_create
  • perform_update
  • serializer_create
  • serializer_update
  • admin_action

批量发送个性化消息

当每个设备需要接收不同标题或正文,但仍需通过 Firebase 批量发送时,请使用 send_bulk_personalized_messages

.. code-block:: python

from fcm_django.models import FCMDevice

FCMDevice.objects.send_bulk_personalized_messages(
    title_template="Hello {name}",
    body_template="You have {count} new messages",
    message_data={
        "token-1": {"name": "Alice", "count": 3},
        "token-2": {"name": "Bob", "count": 7},
    },
    data_fields={"kind": "digest"},
)

message_data 以注册 ID 为键。缺失的模板变量在渲染后的消息中保持不变。

异步查询集批量发送

如果您从异步 Django 视图或其他异步上下文中调用 fcm-django,请使用查询集批量 API 及其异步对应方法:

.. code-block:: python

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

await FCMDevice.objects.filter(user=request.user).asend_message(
    Message(notification=Notification(title="Hi", body="Async batch send")),
)

await FCMDevice.objects.asend_bulk_personalized_messages(
    title_template="Hello {name}",
    body_template="You have {count} updates",
    message_data={"token-1": {"name": "Alice", "count": 3}},
)

这些方法与 FCMDeviceQuerySet 上的 send_messagesend_bulk_personalized_messages 相对应,旨在用于批量查询集操作。

用户订阅或取消订阅主题

.. code-block:: python

from fcm_django.models import FCMDevice

# 订阅
FCMDevice.objects.all().handle_topic_subscription(True, topic="TOPIC NAME")
device = FCMDevice.objects.all().first()
device.handle_topic_subscription(True, topic="TOPIC NAME")

# 最后,您可以向该主题发送消息
from firebase_admin.messaging import Message
FCMDevice.send_topic_message(Message(...), "TOPIC NAME")

# 取消订阅
FCMDevice.objects.all().handle_topic_subscription(False, topic="TOPIC NAME")
device = FCMDevice.objects.all().first()
device.handle_topic_subscription(False, topic="TOPIC NAME")

向主题发送消息

.. code-block:: python

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

FCMDevice.send_topic_message(Message(data={...}), "TOPIC NAME")

额外参数

您可以添加 additional_registration_ids(序列)来手动发送注册 ID。它会将这些 ID 追加到查询集查找返回的注册 ID 中。

您还可以添加 skip_registration_id_lookup(布尔值)以跳过与查询相关的数据库查找。

.. code-block:: python

from firebase_admin.messaging import Message from fcm_django.models import FCMDevice FCMDevice.objects.send_message(Message(...), False, ["registration_ids"])

使用多个 FCM 应用

默认情况下,消息将使用默认的 FCM firebase_admin.App(我们已在设置中初始化)发送,或者使用 DEFAULT_FIREBASE_APP 设置指定的应用。

通过在调用 send_message 时指定应用,可以覆盖此默认设置。这可用于使用不同的 Firebase 项目发送消息。

.. code-block:: python

from firebase_admin import initialize_app from firebase_admin.messaging import Message, Notification from fcm_django.models import FCMDevice

secondary_app = initialize_app(..., name="messaging")
device = FCMDevice.objects.all().first()
device.send_message(
    Message(notification=Notification(title="Hi", body="Secondary app")),
    app=secondary_app,
)

为 FCM 设置默认 Firebase 应用

如果您希望将特定的 Firebase 应用用于所有 fcm-django 请求,可以创建一个 firebase_admin.App 实例,并通过 DEFAULT_FIREBASE_APP 设置将其传递给 fcm-django。

DEFAULT_FIREBASE_APP 将用于所有发送/订阅/取消订阅请求,包括 FCMDevice 的管理操作。

在您的 settings.py 中:

.. code-block:: python

from firebase_admin import initialize_app, credentials from google.auth import load_credentials_from_file from google.oauth2.service_account import Credentials

# 创建自定义 Credentials 类以加载非默认的 Google 服务账户 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)

# 初始化默认的 firebase 应用
# 这会使用 GOOGLE_APPLICATION_CREDENTIALS 环境变量加载默认的 Google 服务账户
FIREBASE_APP = initialize_app()

# 为 fcm-django 初始化第二个 firebase 应用
# 环境变量包含自定义 Google 服务账户 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,
    # [...] 您的其他设置
}

Django REST Framework (DRF) 支持

视图集有两种不同类型:

  • FCMDeviceViewSet

    • 权限遵循您的 Django REST Framework 配置
    • 设备可以注册,无需将其与用户关联
    • 不允许重复的 registration_id
  • FCMDeviceAuthorizedViewSet

    • 权限为 IsAuthenticated 和自定义权限 IsOwner,仅允许 request.user 获取和更新属于该用户的设备
    • 要求用户进行身份验证,因此所有设备都将与用户关联
    • 重复注册 ID 时会更新设备

可以通过以下两种方式添加路由:

  • Routers_(包含所有视图)

.. _Routers: http://www.django-rest-framework.org/tutorial/6-viewsets-and-routers#using-routers

.. code-block:: python

from fcm_django.api.rest_framework import FCMDeviceAuthorizedViewSet

from rest_framework.routers import DefaultRouter

router = DefaultRouter()

router.register('devices', FCMDeviceAuthorizedViewSet)

urlpatterns = [
    # URL 将显示在 <api_root>/devices
    # 列出所有可用端点的 DRF 可浏览 API
    path('', include(router.urls)),
    # ...
]
  • 使用 as_view_(指定要包含的视图)

.. _as_view: http://www.django-rest-framework.org/tutorial/6-viewsets-and-routers#binding-viewsets-to-urls-explicitly

.. code-block:: python

from fcm_django.api.rest_framework import FCMDeviceAuthorizedViewSet

urlpatterns = [
    # 仅允许已认证用户创建设备
    path('devices', FCMDeviceAuthorizedViewSet.as_view({'post': 'create'}), name='create_fcm_device'),
    # 详情路由必须包含视图集使用的查找字段
    path(
        'devices/<str:registration_id>',
        FCMDeviceAuthorizedViewSet.as_view({'delete': 'destroy'}),
        name='delete_fcm_device',
    ),
    # ...
]

使用重复注册 ID 更新设备

令牌是设备特定的,因此,如果用户在您的设备上登出其账户,而另一位用户在同一设备上登录,您不希望旧用户在登出状态下收到消息。

通过 DRF,任何使用已存在注册 ID 创建设备的操作都将转换为更新操作。如果手动操作,则您需要负责删除旧的设备条目。

使用自定义 FCMDevice 模型

如果您需要自定义设备模型,请参阅 使用自定义 FCMDevice 模型

Python 3 支持

  • fcm-django 完全兼容 Python 3.10+
  • 已停止支持 Python 3.9,因为 Python 3.9 已达到生命周期结束。
  • 对于 Python 3.6,请使用 fcm-django < 2.0.0,因为 6 版 firebase-admin 放弃了对 Python 3.6 的支持
  • 对于 Python 3.7 + 3.8,请使用 fcm-django <= 2.2.1

Django 版本兼容性

兼容 Django 4.2+ 版本。 对于 Django 2.2 版本,请使用 fcm-django < 1.0.13。 对于更低的 Django 版本,请使用 fcm-django < 1.0.0

需要帮助、有任何问题或建议?

请在此项目上提交 issue/PR。请勿向我发送电子邮件,因为这样社区将没有机会看到您的问题/提供答案。

贡献指南

设置开发环境:

  • 使用 python3 -m venv env 创建虚拟环境
    • 在 Windows 的 Powershell 中,使用 source env/bin/activate.\env\Scripts\activate.ps1 激活虚拟环境
    • 运行 pip install -r requirements_dev.txt

要手动运行预提交钩子,请运行 pre-commit run --all-files

由于可能使用交换模型,因此测试包含两个配置文件:

  1. 使用默认设置和非交换模型 settings/default.py
  2. 仅包含 swapper 所需的覆盖设置 settings/swap.py

要在本地运行测试,您可以使用 pytest,如果需要在不同数据库上检查迁移,则必须指定环境变量 DATABASE_URL,例如

.. code-block:: console

export DATABASE_URL=postgres://postgres:postgres@127.0.0.1:5432/postgres export DJANGO_SETTINGS_MODULE=tests.settings.default # 或 export DJANGO_SETTINGS_MODULE=tests.settings.swap pytest

为 PyPi 打包

  • 运行 source env/bin/activate
  • 运行 rm -rf dist/
  • 运行 python3 -m build
  • 运行 twine upload dist/*

致谢

本库依赖 firebase-admin-sdk 发送通知,有关所有可能字段的更多信息,请参见: https://github.com/firebase/firebase-admin-python

从 v0 到 v1 的迁移由 Andrew-Chen-Wang 完成。

项目介绍

FCM Django: Send push notifications via django to websites, iOS & android mobile devices through FCM (Firebase Cloud Messaging)

定制我的领域