- Введение
- 🚀 Давайте создадим демонстрационное приложение Django
- mabdullahadeel / django-firebase-notifications
- Использование firebase для отправки уведомлений в реальном времени в приложениях django
- Django Firebase Notifications
- Логика сервера
- Отправка уведомления
- Настройка экземпляра SDK администратора
- Логика на стороне клиента
- Заключение
- Как добавить уведомления в реальном времени с помощью Django и nextjs
- Abdullah Adeel ・ Jul 25 ・ 5 min read
Введение
Недавно я изучал несколько способов добавления функциональности реального времени в приложения Django. Во время поиска я наткнулся на множество сторонних сервисов, таких как PubNub, Pusher, а теперь и Firebase, который я считаю самым интересным из всех.
Ниже приведены критерии, которые я стандартизировал во время принятия решения и сравнения с пользовательскими решениями, такими как django channels. Давайте посмотрим, как Firebase соответствует этим пунктам.
- Масштабируемость : Firebase база данных в реальном времени и firestore, оба могут масштабироваться автоматически.
- Безопасность : Firebase имеет правила безопасности, которые могут быть установлены точно для авторизации ресурсов, и их настройка занимает всего несколько минут. Так что за это большой палец вверх.
- Время на реализацию : Firebase admin SDK хорошо документированы и доступны почти для всех серверных языков. Поэтому вы можете добавить функции реального времени за считанные минуты.
- Удобство обслуживания :С Firebase не требуется абсолютно никакого обслуживания, за исключением некоторых правил безопасности, которые вы, возможно, захотите подправить по мере добавления новых функций.
- Стоимость: Это может быть решающим фактором и может варьироваться от команды к команде или даже от одного проекта к другому. Firebase имеет щедрый бесплатный уровень и планы с оплатой по факту.
🚀 Давайте создадим демонстрационное приложение Django
В этой статье я проведу вас через все шаги, которые необходимо выполнить, чтобы успешно использовать firebase firestore для безопасной отправки уведомлений в реальном времени пользователям в ваших django приложениях.
В ходе тестирования я создал демонстрационное приложение, к которому вы можете обратиться в любой момент, когда окажетесь в затруднительном положении.
Вот ссылка на Github 😸 👇
mabdullahadeel / django-firebase-notifications
Использование firebase для отправки уведомлений в реальном времени в приложениях django
Django Firebase Notifications
Это простое django приложение, которое демонстрирует, как добавить уведомления в реальном времени в django приложения с помощью firebase.
Вот живое приложение 👇
https://django-firebase.vercel.app
Логика сервера
Первая проблема, с которой вы можете столкнуться при попытке внедрить firebase в ваше django приложение — это аутентификация. Ваши пользователи в настоящее время проходят аутентификацию в вашем django приложении, как вы передадите состояние аутентификации в firebase, чтобы он знал о пользователе?
Для решения этой проблемы есть пользовательские токены. Когда клиент успешно авторизуется в вашем django приложении, firebase admin SDK может быть использован для создания токена для клиента. Клиент будет использовать этот токен для подключения к firebase.
Вот как выглядит этот поток.
Полную схему можно посмотреть здесь
Начнем с входа пользователя в систему. Они вводят свои учетные данные для входа во внешнее приложение. Клиент отправляет учетные данные, т.е. имя пользователя/электронную почту и пароль, на бэкэнд для обмена на токен (JWT или простой токен).
Фактическая реализация аутентификации может варьироваться от приложения к приложению, но в моей демонстрации я использую аутентификацию на основе токенов DRF. При успешном входе сервер генерирует два токена, как показано на схеме выше.
- «token» — Этот токен будет использоваться клиентом для аутентификации на сервере django при последующих запросах.
- «firebase_token» — Этот токен будет использоваться клиентом для немедленной автоматической аутентификации на firebase.
Отправка уведомления
Давайте пройдемся по настройкам.
Здесь я предполагаю, что у вас есть аккаунт firebase, что вы и делаете, если у вас есть аккаунт google. Для отправки уведомлений из приложения django мы будем использовать firestore. Если вы еще не настроили проект firebase или firestore, я бы рекомендовал просмотреть это краткое руководство.
Чтобы взаимодействовать с firebase через SDK администратора, вам нужны учетные данные учетной записи сервиса. Чтобы создать учетные данные учетной записи службы, перейдите к учетной записи службы в консоли проекта firebase. Убедитесь, что вы выбрали правильный проект. Выполните следующие действия 👇.
В случае успеха у вас будет загружен файл .json
. Давайте переименуем этот файл в credentials.json
. Обязательно сохраните этот файл, поскольку учетные данные в этом файле дают доступ ко всем ресурсам firebase.
Настройка экземпляра SDK администратора
Выполните следующую команду pip для установки firebase SDK в ваш проект django.
pip install firebase-admin
Затем инициализируйте экземпляр приложения admin, указав путь к файлу credential.json. Если этот файл находится в директории вашего проекта. Обязательно добавьте credentials.json
в ваш файл .gitignore
.
В моем случае я создал вспомогательный класс FirebaseService
в core/firebase/firebase_service.py
для обработки всей логики, связанной с firebase.
import logging
from typing import Any, Dict
from uuid import uuid4
from django.conf import settings
from django.core.cache import cache
import firebase_admin
from firebase_admin import credentials, auth, firestore
from users.models import User
cred = credentials.Certificate(settings.GOOGLE_APPLICATION_CREDENTIALS) # path to credentials.json file
firebase_app = firebase_admin.initialize_app(cred)
auth_client = auth.Client(app=firebase_app)
firestore_client = firestore.client(app=firebase_app)
logger = logging.getLogger(__name__)
def cached(func):
def wrapper(*args, **kwargs):
user = kwargs.get('user')
key = 'token_' + str(user.id)
token = cache.get(key)
if token is None:
token = func(*args, **kwargs)
cache.set(key, token, timeout=60 * 60) # 1 hour
return token
return wrapper
class FirebaseService:
@staticmethod
@cached
def get_custom_token_for_user(user: User):
auth_claims = {
'uid': user.id,
}
return auth_client.create_custom_token(uid=user.id, developer_claims=auth_claims)
@staticmethod
def send_notification_to_user(user: User, message: Dict[str, Any]):
msg_id = str(uuid4())
notification_ref = firestore_client.collection(u'app-notifications')
.document(u'{}'.format(user.id)).collection("user-notifications").document(u'{}'.format(msg_id))
notification_ref.set({
u'message': message,
'id': msg_id
})
logger.info(u'Notification sent to user {}'.format(user.id))
Здесь я загружаю GOOGLE_APPLICATION_CRENDITALS
, которая является путем к файлу credentials.json
из настроек django. А в моем файле settings.py
я загружаю ту же переменную из окружения с помощью django-environ. Вы можете взглянуть на это здесь.
-
Метод
get_custom_token_for_user
отвечает за создание токенов для данного пользователя django. Этот токен затем отправляется клиенту для использования при аутентификации на firebase.create_custom_token
из админки firebase принимает аргументdeveloper_claims
. Все, что вы передали сюда, будет сохранено в полезной нагрузке токена. Но не только это, этот объект/диск будет доступен в объекте firebase request.auth. Это означает, что вы можете получить доступ к этому объекту на клиенте и даже в правилах безопасности firebase.
Вот определение правила firestore, если вам интересно.
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match/app-notifications/{user_id}/{document=**} {
allow delete, read: if
request.auth.uid == user_id
}
}
}
Логика на стороне клиента
На клиенте установите firebase.
yarn add firebase
# or
npm install firebase
# or
pnpm add firebase
Затем в [./firebase/index.ts](https://github.com/mabdullahadeel/django-firebase-notifications/blob/master/client/firebase/index.ts)
я использую учетные данные для инициализации приложения firebase и использую их во всем приложении.
import { initializeApp } from "firebase/app";
import { getFirestore } from "firebase/firestore";
import { getAuth } from "firebase/auth";
const firebaseConfig = {
apiKey: process.env.NEXT_PUBLIC_FB_API_KEY,
authDomain: process.env.NEXT_PUBLIC_FB_AUTH_DOMAIN,
projectId: process.env.NEXT_PUBLIC_FB_PROJECT_ID,
storageBucket: process.env.NEXT_PUBLIC_FB_STORAGE_BUCKET,
messagingSenderId: process.env.NEXT_PUBLIC_FB_MSG_SENDER_ID,
appId: process.env.NEXT_PUBLIC_FB_APP_ID,
};
export const app = initializeApp(firebaseConfig);
export const firestore = getFirestore(app);
export const auth = getAuth(app);
Затем в моей логике аутентификации, при успешном входе/подписании, я автоматически подписываю пользователя в firebase, используя токен, полученный с сервера django.
Подробности смотрите здесь
import { auth as firebaseAuth } from "./firebase";
const initializeFirebaseAuth = async (user: MeResponse) => {
return signInWithCustomToken(firebaseAuth, user.fb_token);
};
Для отображения уведомлений в UI я создаю пользовательский хук, который подписывается на соответствующий канал для получения уведомлений для текущего вошедшего пользователя.
import React, { createContext, useEffect, useState, useContext } from "react";
import { doc, onSnapshot, deleteDoc, collection } from "firebase/firestore";
import { firestore } from "../firebase";
import { useAuth } from "../hooks/useAuth";
interface NotificationPayload {
message: string;
id: string;
}
interface NotificationState {
messages: NotificationPayload[];
unreadCount: number;
resetUnreadCount: () => void;
markAllAsRead: () => void;
markOneMessageAsRead: (id: string) => void;
}
export const NotificationsContext = createContext<NotificationState>({
messages: [],
unreadCount: 0,
resetUnreadCount: () => {},
markAllAsRead: () => {},
markOneMessageAsRead: () => {},
});
export const NotificationProvider: React.FC<React.PropsWithChildren> = ({
children,
}) => {
const [messages, setMessages] = useState<NotificationPayload[]>([]);
const [unreadCount, setUnreadCount] = useState(0);
const { isAuthenticated, user } = useAuth();
useEffect(() => {
let unsubscribe: () => void;
if (isAuthenticated && user?.user) {
unsubscribe = onSnapshot(
collection(
firestore,
"app-notifications",
user.user.id,
"user-notifications"
),
(snapshot) => {
const messages = snapshot.docs.map((doc) => ({
message: doc.data().message,
id: doc.id,
}));
setMessages(messages);
setUnreadCount((prev) => (messages.length ? prev + 1 : 0));
}
);
}
return () => {
if (unsubscribe) {
unsubscribe();
}
};
}, [isAuthenticated, user]);
const resetUnreadCount = () => setUnreadCount(0);
const markAllAsRead = async () => {
if (isAuthenticated && user?.user) {
await Promise.all(
messages.map(async (msg) => {
await deleteDoc(
doc(
firestore,
"app-notifications",
user.user.id,
"user-notifications",
msg.id
)
);
})
);
setMessages([]);
}
};
const markOneMessageAsRead = async (id: string) => {
if (isAuthenticated && user?.user) {
await deleteDoc(
doc(
firestore,
"app-notifications",
user.user.id,
"user-notifications",
id
)
);
}
setMessages(messages.filter((msg) => msg.id !== id));
};
return (
<NotificationsContext.Provider
value={{
messages,
unreadCount,
resetUnreadCount,
markAllAsRead,
markOneMessageAsRead,
}}
>
{children}
</NotificationsContext.Provider>
);
};
export const useFirebaseNotifiactions = useContext(NotificationsContext);
Заключение
Изучение этого нового способа реализации функций реального времени было захватывающим, и, без сомнения, это один из лучших вариантов, когда речь идет о таких критических факторах, как масштабируемость, удобство обслуживания и время реализации.
Если вы хотите протестировать его, вот демонстрация в реальном времени👇
https://django-firebase.vercel.app
Если вы хотите увидеть другой похожий подход с PubNub, прочитайте эту статью👇.

Как добавить уведомления в реальном времени с помощью Django и nextjs
Abdullah Adeel ・ Jul 25 ・ 5 min read
Если вам понравилось то, что вы только что прочитали, почему бы не проследить за вами в Twitter abdadeel_
Спасибо 👋