15

So I'm building a Django progressive web app with offline support using service workers.

According to google's documentation, the sw.js file should be at the root of the app's url:

You need to do this because the scope of a service worker (the set of urls that the ServiceWorker will load for) is defined by the directory where it resides.

At the moment, I'm serving all static assets from http://example.com/static/ folder. But I need to serve this specific file at a url like: http://example.com/sw.js.

Any idea how I can achieve this? I could make a specific nginx rule to do this redirection, but I don't know if it's the cleanest way of doing this. Maybe this setting should reside in urls.py?

Note: I've seen this question which suggests using the static() method from django.conf.urls.static. But django's docs say that the static method is only for development use so not good for me.

Note (2): I guess I could change the STATIC_URL setting, but I'm happy with my files being served from /static directory. I only want this one file to be at url's root.

Community
  • 1
  • 1
gkpo
  • 2,623
  • 2
  • 28
  • 47

4 Answers4

24

You can serve javascript as a view, not just html. Put this in your projects urls.py

url(r'^service-worker.js', cache_control(max_age=2592000)(TemplateView.as_view(
    template_name="service-worker.js",
    content_type='application/javascript',
)), name='service-worker.js'),

Then put your service-worker.js in your templates directory.

Bonus is that you can now also use template tags like static in your javascript file.

dalore
  • 5,594
  • 1
  • 36
  • 38
  • 1
    You might want to remove the cache_control, as by default service workers can't be cached for more than 24 hours and usually you want to serve a new service worker as soon as it is available. – Tiagojdferreira Apr 14 '17 at 12:12
  • 1
    but I want varnish on the frontend to cache that page. Especially since it's going to be requested by every client, every 24 hours. Then you just cache bust the url, i.e. `var sw = navigator.serviceWorker.register('{% url 'service-worker.js' %}?v=13');` – dalore May 19 '17 at 09:33
  • Interesting. Does this have any downside if we're using WhiteNoise? won't it be cached until next deploy? (no ngnix available) – zerohedge Feb 17 '23 at 01:41
5

Django 2.2

project structure

myproj/
|-app/
| |-templates/
|   |-app/
|     -sw.js
|-myproj/
  -urls.py

urls.py (project)

from django.views.generic import TemplateView

urlpatterns = [
  ...
  path('sw.js', (TemplateView.as_view(template_name="app/sw.js", 
  content_type='application/javascript', )), name='sw.js'),
]
Taylor A. Leach
  • 2,115
  • 4
  • 25
  • 43
4

In Django 1.11 urls.py should look:

from django.views.generic import TemplateView

urlpatterns = [
  url(r'^sw.js', (TemplateView.as_view(template_name="sw.js", content_type='application/javascript', )), name='sw.js'),
]
Santiago M. Quintero
  • 1,237
  • 15
  • 22
3

I was getting all the time the error DOMException: The script resource is behind a redirect, which is disallowed.

I spent hours trying to figure out the solution.

Apart from adding at urls.py:

from django.views.generic import TemplateView

urlpatterns = [
  ...,
  url(r'^service-worker.js', (TemplateView.as_view(template_name="service-worker.js", content_type='application/javascript', )), name='service-worker.js'),
]

There was also another step needed. Instead of

<script>
 if ('serviceWorker' in navigator) {
    console.log("Will the service worker register?");
    navigator.serviceWorker.register('service-worker.js')
      .then(function(reg){
        console.log("Yes, it did.");
     }).catch(function(err) {
       console.log("No it didn't. This happened:", err)
        console.log("err.message:", err.message)
    });
 }
</script>

I used:

<script>
 if ('serviceWorker' in navigator) {
    console.log("Will the service worker register?");
    navigator.serviceWorker.register("{% url 'service-worker.js' %}") //note that I am using the url template here
      .then(function(reg){
        console.log("Yes, it did.");
     }).catch(function(err) {
       console.log("No it didn't. This happened:", err)
        console.log("err.message:", err.message)
    });
 }
</script>
J0ANMM
  • 7,849
  • 10
  • 56
  • 90