Combining css and javascript in Django¶

I’m handling Django and css/javascript files with django-staticfiles. This makes it easy to ship javascript and css files (and images) with your Django applications.

The end result, for me, is quite a number of css and javascript files.

Blueprint css framework has two css files.

Jquery; jquery ui; openflow jquery tools; openlayers. All javascript files.

A jquery tree extention and one or two that I’ve forgotten about.

Two or three reusable apps, each with a css and js file.

Ehm? That’s not good for the responsiveness of the website, as it increases the number of server requests that need to happen. And the number of requests is nowadays often a big percentage of the time it takes to load a page.

The basic solution: combine the javascript and css files into one javascript and one css file. (Well, css files need to be split a bit according to their media="screen, presentation" or media="print" settings).

There are a number of Django solutions. I went with django_compressor as I found a snippet of code that showed how to integrate django_compressor with django-staticfiles. (Warning as I made that mistake: one has an underscore, one has a dash in the name…)

The basic usage in templates is simple. I’m using a custom base UI django application for all our websites with a base template. I just had to add django_compressor’s statement in there around my css/js blocks and I was done:

<!DOCTYPE html> <html> <head> <title> {% block title %} Sample title {% endblock title %} </title> {% load compress %} {% compress css %} {% block css %} {# Blueprint css framework. #} <link rel="stylesheet" href="{{ STATIC_URL }}blueprint/screen.css" type="text/css" media="screen, projection" /> <link type="text/css" href="{{ STATIC_URL }}jquery-treeview/jquery.treeview.css" rel="stylesheet" media="screen, projection" /> <!-- And a couple of other stylesheets, of course --> {% endblock css %} {% endcompress %} {% compress js %} {% block javascript %} <script type="text/javascript" src="{{ STATIC_URL }}jquery/jquery.js"></script> <!-- Yeah, and more js, of course --> {% endblock javascript %} {% endcompress %} ...

And in the settings.py :

# Storage engine to be used during compression COMPRESS_STORAGE = "staticfiles.storage.StaticFileStorage" # The URL that linked media will be read from and compressed # media will be written to. COMPRESS_URL = STATIC_URL # The absolute file path that linked media will be read from # and compressed media will be written to. COMPRESS_ROOT = STATIC_ROOT

Note that all three statements depend on the django-staticfiles setup.

Bonus for django_compressor: it not only combines the js/css, but it also gives the static file that it generates a unique name that depends on the timestamp of all the combined resources. So every time something changes in one of the source files, a new combined file with a new name is generated. So you can cache it forever. This means that your users will only have to download one css and one js file once, basically. A huge performance boost.

A bit of simple apache config that I added for this caching:

< Location / static_media /> # This is the regular django-staticfiles directory. # I set caching for one day here. That works well enough # with our update frequency at the moment. ExpiresActive On ExpiresDefault "access plus 1 day" </ Location > < Location / static_media / CACHE /> # This is the location where django_compressor places # the combined files it generates. Every one of them # can be cached till the day the coyote finally catches # the roadrunner. ExpiresActive On ExpiresDefault "access plus 10 years" </ Location >

(You can get much more fancy with varnish caching and more cache control headers, but this works well enough for now. I’m all ears for better setups, though :-)

Side note: using django_compressor means a bit of settings.py boilerplate in your projects. And some apache config boilerplate. Why don’t you make it easy for yourself and read up on what I wrote about software skeletons for easy creation of all your boilerplate?