How to make a Django application run offline using service workers
I was working on an application recently and I needed to make the app a progressive web application. I researched on how to make that possible for a Django application but couldn’t find relevant help.
After watching a series of videos on how to make it work using JavaScript, I was able to successfully make one that works for Django. I will show you how I did it in this article.
NOTE: I assume that you already have a Django project started and that you are familiar with using Django.
To make a progressive web app, you need two important files — service worker and web app manifest file.
What is a Service Worker?
A service worker is a script that your browser runs in the background, separate from a web page, opening the door to features that don’t need a web page or user interaction.
During development, you’ll be able to use service worker through localhost, but to deploy it on a site you'll need to have HTTPS setup on your server. Learn more about service worker
What is a manifest file?
The web app manifest is a JSON file that tells the browser about your Progressive Web App and how it should behave when installed on the user’s desktop or mobile device. A typical manifest file includes the app name, the icons the app should use, and the URL that should be opened when the app is launched.
Now let’s bring these all to Django…
- Create a manifest.webmanifest file in your static folder
{"name": "Name of your app","short_name": "APP_NAME","start_url": "http://localhost:8000/","display": "standalone","orientation": "portrait","background_color": "#fff","description": "Enter the description of your app here.","icons": [{"src": "avatar.png","sizes": "2700x2700","type": "image/png"},{"src": "avatar_02.png","sizes": "192x192","type": "image/png"}]}
In the manifest.webmanifest file above, the start_url is the path to where the service worker view points to. This will be done in the next step.
2. Create an sw.js file in your template folder
const cacheName = 'YOUR_APP_NAME';function precache() {return caches.open('my-cache').then(function (cache){return cache.addAll([ '{% url "home" %}', '{% url "about" %}', '{% url "contact" %}',
// YOU CANN ADD YOUR URLs HERE. ]);});}{% load static %}const staticAssets = ['{% static "css/style.css" %}',
'{% static "css/bootstrap.min.css" %}',
'{% static "js/bootstrap.min.js" %}',// YOU CAN ADD ALL YOUR STATIC FILES HERE];self.addEventListener('install', async e => {const cache = await caches.open(cacheName);await cache.addAll(staticAssets);return self.skipWaiting();});self.addEventListener('activate', e => {self.clients.claim();});self.addEventListener('fetch', async e => {const req = e.request;const url = new URL(req.url);if (url.origin === location.origin) {e.respondWith(cacheFirst(req));} else {e.respondWith(networkAndCache(req));}});async function cacheFirst(req) {const cache = await caches.open(cacheName);const cached = await cache.match(req);return cached || fetch(req);}async function networkAndCache(req) {const cache = await caches.open(cacheName);try {const fresh = await fetch(req);await cache.put(req, fresh.clone());return fresh;} catch (e) {const cached = await cache.match(req);return cached;}}
If you want to know more about the service worker javascript code, click here
3. Create a service worker view in your app views.py
from django.views.generic.base import TemplateView"""Service worker for offline app"""
class ServiceWorker(TemplateView):
template_name = "mobile/sw.js"
content_type = "application/javascript"
In the view above, we used a generic class base view to render the sw.js as a template. Now let’s edit the urls.py …
4. Create a URL for the ServiceWorker view in urls.py
from django.urls import path
from django.contrib import admin
from your_app.views import ServiceWorkerurlpatterns = [
path('admin/', admin.site.urls),
path('', ServiceWorker.as_view(), name="sw"),
]
NOTE: It is important to put the service worker URL path as the root directory. This will make it possible for the service worker to work for every other directory in the project e.g https://yoursite.com/polls
5. Putting it all together
The service worker needs to be registered in our HTML file. Now go to your index.html file and make the following edits.
<!DOCTYPE html>
<html lang="en"><head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
//Put the manifest.webmanifest file here
<!--LINK TO MANIFEST (FOR PWA)-->
<link rel="manifest" href="{% static 'manifest.webmanifest' %}"><!-- SERVICE WORKER JS -->
<script>// REGISTER SERVICE WORKERif ('serviceWorker' in navigator) {window.addEventListener('load', function() {navigator.serviceWorker.register('{% url "sw"%}').then(function(registration) {// Registration was successfulconsole.log('ServiceWorker registration successful with scope: ', registration.scope);}, function(err) {// registration failed :(console.log('ServiceWorker registration failed: ', err);});});}</script>
<!-- END SERVICE WORKER --></head><body></body>
</html>
Now, the service worker should be properly registered on your browser.
And there you go, your progressive web app with Django…
When you run your app in your browser, you see the following:
- You’ll notice a “+” button (marked with a red circle) at the top right corner. This button is the button you will use to download the app to your desktop or mobile phone
- Using the inspect tools, navigate to the application tab, (marked with the green rectangle). You’ll see your manifest.webmanifest data showing up. Your app name and other information.
- The service worker can be seen by clicking the “Service Worker” button on the right. You will see this
Just in case you don’t get the desired result, please go through the steps all over again.
Cheers and happy coding! 😉