django-require 1.0.0 has landed

182 views
Skip to first unread message

Dave Hall

unread,
Nov 1, 2012, 2:42:23 PM11/1/12
to django-...@googlegroups.com
The first production release of django-require is now available for download from GitHub and PyPi.

django-require is a Django staticfiles post-processor for optimizing with RequireJS.

Features:
  • Optimize your static assets using the excellent r.js optimizer.
  • Compile standalone modules using the almond.js shim.
  • Compatible with any Django staticfiles storage backend.
Since this is any early release, feedback and feature requests are very welcome. The project site for django-require is hosted on GitHub, here:


Happy coding!

Dave.

Nathaniel Tucker

unread,
Nov 2, 2012, 2:25:01 AM11/2/12
to django-...@googlegroups.com
Thanks for making this! I think you're my savior. Can you make add documentation on how to use this with django-compressor?

Dave Hall

unread,
Nov 2, 2012, 5:59:21 AM11/2/12
to Nathaniel Tucker, django-...@googlegroups.com
I don't think that you'd really want to use this with django-compressor. To my understanding, django-compressor is a utility for joining and minifying multiple JS and CSS files. That's exactly what django-require's optimizer does too, but in a different way!

If you're using RequireJS in your project, then you already know about using AMD style to separate your modules into separate files, importing on module into another using define() and require() calls. When you run ./manage.py collectstatic on your project, django-require uses the RequireJS optimiser to minify all your javascript, then join all javascript files together in the correct order to satisfy their dependencies. It does a similar thing with all your CSS files, based on their @import directives.

To use an example (first with CSS, because it's simpler), in django-compressor, you'd do something like this:

{% compress css %}
<link rel="styleshee" href="/foo.css">
<link rel="styleshee" href="/bar.css">
{% endcompress %}

Whereas using django-require, you'd instead simply do this:

<link rel="styleshee" href="/bar.css">

And at the top of bar.css, @import url(/foo.css). During development, foo.css will be loaded dynamically from bar.css, but after optimization, foo.css will actually be inlined at the top of bar.css, and the whole lot minified. This is actually much simpler to code in your templates, and is particular suited for cloud-based environments like heroku, where pausing a async worker to compile a load of CSS on the fly is a bad idea!

Now, to use a javascript example, you'd traditionally split your javascript files into modules and export their public API into a global variable, something like this:

// foo.js
(function() {
    var exports = {
        foo: function() {
            console.log("foo");
        }
    }
    window.fooModule = exports;
}());

// bar.js
(function()  {
    fooModule.foo();
}());

You'd then load this modules in your template like so:

{% compress js %}
<script src="/foo.js"></script>
<script src="/bar.js"></script>
{% endcompress %}

Using django-require, you'd define your modules using the AMD syntax, like so:

// foo.js
define(function() {
    return {
        foo: function() {
            console.log("foo");
        }
    }
});

// bar.js
require(["foo"], function(fooModule) {
    fooModule.foo();
});

And then, in your templates, you'd do this:

{% require_module "bar" %}

Which would get rendered as this:

<script data-main="/bar.js" src='/require.js"><script>

When you run the collectstatic, the RequireJS optimizer will detect that bar.js requires foo.js, and join the two scripts together with foo.js ahead of bar.js. This style encourages code re-use, and has many other benefits out outlined on the RequireJS website (http://requirejs.org/).

As it happens, you can use django-require to optimize your code, even if you don't use a single AMD definition in your code at all! Even without a build profile, it will still minimise all JS and CSS in your project. However, once your start using RequireJS to organise your code, you'll quickly see that it's worth investing in the syntax and methodology.

tl;dr; - There's no point using django-require with django-compressor, they're both doing the same thing in different ways (although RequireJS is way cooler than the old window-globals style of modular javascript!).

Best,

Dave.



--
 
 

Nathaniel Tucker

unread,
Nov 2, 2012, 9:35:29 AM11/2/12
to django-...@googlegroups.com, Nathaniel Tucker
So how do I specify the modules I need in one file beforehand, and then get it to generate a different filename every time so my CDN doesn't get stale?

Dave Hall

unread,
Nov 2, 2012, 10:47:17 AM11/2/12
to Nathaniel Tucker, django-...@googlegroups.com
If you use RequireJS correctly, then the modules you need are implicit in your calls to require() and define() from within your javascript modules. Are you using RequireJS in your site already? If so, then you should already have at least main.js file or equivalent as the main top-level module for your site.

You can tell the django-require optimizer to then compile and compress main.js and all it's dependencies into one large file, which will be called main.js in your STATIC_ROOT. You do this by specifying a build profile for your project.


In this build profile, you can specify "main" in the list of modules, and it will be built and compiled during the optimization step.

In terms of fingerprinting your files so that they can be served using a CDN, you can use the django.contrib.staticfiles.storage.CachedFilesMixin, to fingerprint your newly-built main.js file with an MD5 hash of it's contents.


django-require is actually a very lightweight build layer on top of the normal RequireJS optimizer. You should familiarise yourself with the RequireJS docs on optimization here:



--
 
 

TAH

unread,
Dec 6, 2012, 5:36:58 PM12/6/12
to django-...@googlegroups.com
Hey Dave --

This is awesome -- and your work came just in time for my project!

Question regarding Heroku -- how to get this to run there?  I'm running into errors because my Django Heroku environment can't find node or rhino to run on the files.  Do I need to spin up a node worker to run collectstatic on?

Thanks --

T.

Dave Hall

unread,
Dec 7, 2012, 4:45:51 AM12/7/12
to TAH, django-...@googlegroups.com
Hi,

I deploy this on heroku all the time. My own approach is to use amazon S3 for storage, since it keeps my dynos clear of static files. My approach for this is documented in the readme here:


With this setup, you just run collectstatic on your own client machine.

Otherwise, if you really want to serve static files from your dyno, then make sure you're using the rhino environment, and change your Procfile to as follows:

web: python manage.py collectstatic --noinput; python manage.py runserver

Hopefully you're no using the built-in dev server to run your instance, so replace the last command with whatever was on your web line before.


--
 
 

TAH

unread,
Dec 7, 2012, 8:23:21 PM12/7/12
to django-...@googlegroups.com, TAH
Thanks for your response.

That is what I am doing -- pushing everything to S3.  Every one of my environments has a different S3 bucket for site assets, so my Procfile has the following to push the assets to the Heroku-specific bucket:

web: [ $LOCAL_STATIC == yes ] && python manage.py collectstatic --noinput; gunicorn treelines.wsgi -b 0.0.0.0:$PORT

This gives this error:

-----> Collecting static files
       Traceback (most recent call last):
       OSError: [Errno 2] No such file or directory
 !     Error running manage.py collectstatic. More info:
       http://devcenter.heroku.com/articles/django-assets


Which I am assuming (though I haven't traced it) to be related to the inability to run node on a Django dyno.

Are you saying I have to switch my local environment to use the production environment's bucket and run collectstatic locally?

Thanks!

Nathaniel Tucker

unread,
Dec 8, 2013, 3:41:49 AM12/8/13
to Dave Hall, django-...@googlegroups.com
What about precompilers like to handle less, or handlebars?

Currently I have:
COMPRESS_PRECOMPILERS = (
    ('text/less', 'lessc {infile} {outfile}'),
    ('text/x-handlebars', 'node_modules/handlebars/bin/handlebars {infile} -f {outfile} -k each -k if -k unless -k with'),
)

--
-Nate the Great

Dave Hall

unread,
Dec 8, 2013, 9:01:55 AM12/8/13
to Nathaniel Tucker, django-...@googlegroups.com
django-require is just an adapter for RequireJS, with all the features (and lack thereof), that implies.

That being said, it's possible to use RequireJS for your LESS and Handlebars templates via the appropriate loader plugins. Googling for requirejs-handlebars gives you a load of options.

For LESS loading and compiling, I actually went and created my own loader plugin, which you're welcome to use. I'll be releasing it as open source soon, but for now, you can get it here: https://gist.github.com/etianen/7005941


--
You received this message because you are subscribed to the Google Groups "django-require discussion and announcements" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-requir...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

Reply all
Reply to author
Forward
0 new messages