2General

Want to know more about us? Visit 2general.com »

Compiling Dust.js templates with django-mediagenerator

Here at 2General we’ve used django-mediagenerator as the asset manager in many of our latest projects. It combines the best parts of Django’s own staticfiles implementation and the older django-compressor app.

Dust.js is a fast, asynchronous JavaScript-based templating engine which runs both in browsers and on Node.js. We haven’t used Dust.js extensively yet, but we immediately noticed that if we’re planning to use it, we need to compile Dust.js templates during our normal build process, i.e. be able to add them in our mediagenerator bundles.

Jump to code example »

Use our improved django-mediagenerator fork

Unfortunately django-mediagenerator project is somewhat abandoned as its original authors decided to call it quits last year. However, there’s an “official” Github fork which hasn’t received any updates yet and our own fork which we have updated with these improvements:

  • Improved performance of the development mode (MEDIA_DEV_MODE=True) through caching
  • Support for compiling Dust.js templates in JavaScript bundles

Django-mediagenerator assets (usually JavaScript files and stylesheets) are organized into “bundles” in your settings.py. On a production server all the files in a bundle are served as one concatenated and minified file. The created files are versioned with a hash, so django-mediagenerator also effectively handles possible caching issues with your users.

One of the most powerful features of django-mediagenerator is that it’s able to run the source files through filters, for example to compile LESS/SASS files. When you’re running your Django server in development mode, django-mediagenerator does this on the fly.

Install our django-mediagenerator fork with pip:

$ pip install -e git+git://github.com/2general/django-mediagenerator.git#egg=django-mediagenerator

Using django-mediagenerator in production and in development

On your production server, use manage.py generatemedia to create the concatenated and minified scripts and stylesheets. The files will be created in the _generated_media/ directory in the root of your Django project. The management command also copies all your other static files under that directory. Make sure you set up your web server to make the contents of that directory visible to the outside world.

generatemedia also creates a _generated_media_names.py file that contains a dict mapping original file names to the copied and versioned file names. The django-mediagenerator middleware makes sure this dict is available for your application.

When developing your application, set MEDIA_DEV_MODE=True in your settings.py. In this mode django-mediagenerator will link to each script and stylesheet separately and won’t minify them. Some of the filters are still run, for example compiling LESS/SASS files and Dust.js templates.

Using Dust.js compiler

For some reason Dust.js didn’t ship with a command line compiler, so the first thing was to create one. Using lessc as an example, Antti made the dustc command line compiler. It was recently pulled into LinkedIn’s Dust.js fork which seems to be the most actively developed version at the moment and recommended over akdubya’s original version.

Set up your virtualenv so that Node.js packages like Dust.js get installed into your virtual environment. When you’ve done that, install LinkedIn’s Dust.js fork like this:

$ npm install -g dustjs-linkedin

Code example

index.html (our Django template)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
{% load media %}
<!DOCTYPE html>
<html>
<head>
    ...
</head>
<body>

    <ul id="people">
    </ul>

    {% include_media "my_script_bundle.js" %}
</body>
</html>

person_tmpl.html (a Dust.js template file)

1
2
3
4
5
{#people}
<li>
    {lastname}, {firstname}
<li>
{/people}

script.js (our main JavaScript)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// list of people to pass to the template
var peopleList = [
    { firstname: "Sauli", lastname: "Niinistö" },
    { firstname: "Tarja", lastname: "Halonen" },
    { firstname: "Martti", lastname: "Ahtisaari" },
    { firstname: "Mauno", lastname: "Koivisto" }
];

// asynchronous handler for Dust.js renderer
dust.render(
    "person_tmpl", // name of the compiled template object
    { people: peopleList }, // context object to pass to the template
    function(err, out) { // handler for output
        $("#people").append(out);
    }
);

settings.py (Django settings file, see django-mediagenerator docs for other configuration)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
MEDIA_BUNDLES = (
    (
        'my_script_bundle.js',
        'jquery.js', 'dust-core-0.4.0.js', {
            'filter': 'mediagenerator.filters.dust.DustFilter',
            'name': 'person_tmpl.html',
            'template_name': 'person_tmpl',
        }, 'script.js'
    ),
)

Result

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
...

<ul id="people">
    <li>
        Niinistö, Sauli
    <li>
    <li>
        Halonen, Tarja
    <li>
    <li>
        Ahtisaari, Martti
    <li>
    <li>
        Koivisto, Mauno
    <li>
</ul>

...