I have HTML/JS/CSS files provided by a third party (whom I have no control over) that serves as a single page app that communicates with a backend built with Django and django-rest-framework.
I'm wanting to host this on Heroku and thus these static assets are being served by Django. These files contains relative paths to each other. For example, the index.html contains:
<link rel="stylesheet" type="text/css" media="screen" href="styles/css/bootstrap.min.css">
Which leads to a 404 because styles/css/bootstrap.min.css
is not routed by django.
The only way I know of to serve the index.html from my domain root www.domain.com
is with an url config like:
url(r'^$', TemplateView.as_view(template_name='index.html'), name='home'),
...even though it's not really a template, it's just plain HTML.
The problem arises from the fact that all the urls in the other assets are relative to this index.html
and of course Django doesn't work like that. If I was developing this front-end application I'd be using the static
template tag and one of the various ways to get urls to javascript.
I don't mind switching from Heroku to another PaaS if they offer a solution to this problem, but manually editing all these files does not sound like a fun job...especially considering the fact that I'll be receiving updates to these files going forward.
I think the way to solve this on a regular old server would be to configure the web server to resolve these urls correctly, but that option doesn't seem to be available on Heroku.
Here's how to set up Django to serve your static files and index.html on / while still having the possibility to use Django views for the admin dashboard, registration etc.
I specify
urlpatterns
as a list, as Django 1.10 requires. The redirects are not permanent by default since 1.9, so you need to explicitly setpermanent=True
if you want browsers to cache this, though when debugging I find it better to start withFalse
.This approach allows you to use something like create-react-app or Yeoman frontend generators that package a built, minified frontend app into a single folder (like dist/). You then e.g. use a script to move that into Django's static files folder (e.g. myproject/static/) and serve it from Heroku.
What Ian wrote about wanting to use something like S3 for your static files stands, but sometimes you just want to start simple with one repository, one Heroku dyno and still be able to use Django + a SPA. Also, using something like WhiteNoise makes serving static files from Python pretty OK and allows you to later on easily put a CDN in front of your static files.
Note: for user-uploaded files you should still use an outside service like Amazon S3 or Backblaze B2 (which is 4 lines of code to integrate).
Note 2: on production with
DEBUG=False
there are issues, so check out this WhiteNoise issue for a solution.Note 3: Turned this answer into a blog post here.
Note 4: since writing this, I've been tweaking the solution more and more for frontend routing, so I ended up releasing a new django-spa package suited for simple serving of single-page apps from Django.