Implementing web push notifications in a Django project using Django Rest Framework (DRF) and React without relying on external service providers can be achieved using the django-webpush package. Here’s a detailed guide to help you set it up.
Step 1: Install Required Packages
First, install the django-webpush package.
Step 2: Configure Django Settings
Add webpush to your INSTALLED_APPS in settings.p
Step 3: Add URLs and Views
Add the necessary URLs for subscribing and sending notifications in urls.py.
from django.urls import path
from webpush.views import save_info
urlpatterns = [
# Other URLs
path('webpush/save_information/', save_info, name='save_webpush_info'),
]
Step 4:
Create a view to handle sending notifications. You can create a new file views.py or add to an existing one.
from django.shortcuts import get_object_or_404
from webpush import send_user_notification
from django.contrib.auth.models import User
from django.http import JsonResponse
def send_push_notification(request, user_id):
user = get_object_or_404(User, pk=user_id)
payload = {"head": "Welcome!", "body": "Hello World!", "icon": "path/to/icon.png", "url": "
https://example.com"}
send_user_notification(user=user, payload=payload, ttl=1000)
return JsonResponse({"status": "Notification sent"})
Step 5:
Create Push Manager on the Frontend
In your React application, you need to add service worker logic to handle push notifications.
- Create a Service Worker (service-worker.js):
self.addEventListener('push', function(event) {
const data = event.data.json();
const options = {
body: data.body,
icon: data.icon,
data: { url: data.url },
};
event.waitUntil(
self.registration.showNotification(data.head, options)
);
});
self.addEventListener('notificationclick', function(event) {
event.notification.close();
event.waitUntil(
clients.openWindow(event.notification.data.url)
);
});
Register Service Worker and Subscribe to Push Notifications in React:
// public/service-worker.js must be at the root level of your public directory
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/service-worker.js')
.then(registration => {
console.log('ServiceWorker registration successful with scope: ', registration.scope);
return registration.pushManager.getSubscription()
.then(subscription => {
if (subscription) {
return subscription;
}
const vapidPublicKey = '<Your VAPID Public Key>';
const convertedVapidKey = urlBase64ToUint8Array(vapidPublicKey);
return registration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: convertedVapidKey,
});
});
})
.then(subscription => {
fetch('/webpush/save_information/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
subscription: subscription,
user: user_id, // Pass the user id from your frontend
}),
});
})
.catch(error => {
console.log('ServiceWorker registration failed: ', error);
});
});
}
function urlBase64ToUint8Array(base64String) {
const padding = '='.repeat((4 - base64String.length % 4) % 4);
const base64 = (base64String + padding)
.replace(/-/g, '+')
.replace(/_/g, '/');
const rawData = window.atob(base64);
const outputArray = new Uint8Array(rawData.length);
for (let i = 0; i < rawData.length; ++i) {
outputArray[i] = rawData.charCodeAt(i);
}
return outputArray;