django-grunt

Django tools for integrating Grunt into your workflow


License
MIT
Install
pip install django-grunt==1.1.0

Documentation

django-grunt

django-grunt provides settings, management commands, a view mixin and a template context processor to improve your workflow using Grunt with Django.

Motivation

Grunt is great for managing tasks that manipulate your project's static files. You can use its plugins to compile LESS or SASS into CSS. You can minify JavaScript or build production files for use with RequireJS. The possibilities are limitless if you put in the effort to build your own Grunt plugins.

The problem is that typical Django projects use the staticfiles app for managing static files. Static files are usually stored in several locations without a common directory ancestor. Before you can use Grunt to manipulate your static files, you need to run the collectstatic management command to gather all of your static files into one common directory.

After running Grunt to process your collected static files, you need to change some settings and use the runserver management command to test out the processed files in your browser.

django-grunt helps to simplify this process with management commands and template context variables.

Installation and Usage

django-grunt is available on PyPI, so you should install it with pip:

pip install django-grunt

To use the management commands, you should add "grunt" to INSTALLED_APPS in your settings file.

Management Commands

django-grunt provides two management commands to simplify your workflow:

manage.py grunt [grunt command ...]

This command runs Django's collectstatic command and then invokes grunt with the given [grunt command ...] labels.

If you have a Grunt build command set up in your Gruntfile.js configuration, you can run manage.py grunt build to collect your static files into the settings.STATIC_ROOT directory and then run grunt build on them.

manage.py gruntserver [runserver argument ...]

This command configures your project's settings so that runserver will serve the files from settings.STATIC_ROOT and then invokes runserver.

It also configures some settings values that you can pass to your templates with the provided view mixin or context processor. These values can be used by your templates to decide which CSS and JavaScript sources to load.

The command also turns on Django's GZipMiddleware so that you can simulate a proper gzip setup on your production server. This can help you get an accurate feel for your project's static files payload in a production environment.

Template context variables and settings

django-grunt provides a view mixin and context processor that you can use to pass Grunt-related context variables to your templates. Both methods set boolean values named grunt_js and grunt_css, which your templates can use to decide whether to serve "normal" static files or static files which have been processed by Grunt. These values are False by default. Generally, they'll be False in development and True in your production server's settings file. The gruntserver management command sets them to True so you can simulate your production setup.

The view mixin is located in grunt.views.mixins.GruntConfigMixin. You should call its grunt_config() method to retrieve or update a dictionary of context variables. It accepts an optional dictionary to update in the config parameter.

If your project uses class-based views, a useful pattern is to override Django's generic views with additional mixins that provide global configuration data to all of your templates. As an example, suppose you have a core application in your project and that it defines generic view overrides. Your core.views module can provide its own TemplateView using the GruntConfigMixin like so:

from django.views import generic

from grunt.views.mixins import GruntConfigMixin


class ConfigMixin(GruntConfigMixin, generic.base.ContextMixin):
    """
    Global configuration context mixin for all generic views.

    """
    def get_context_data(self, **context):
        return self.grunt_config(
            super(ConfigMixin, self).get_context_data(**context))


class TemplateView(ConfigMixin, generic.TemplateView):
    pass

Now, rather than inheriting from django.views.generic.TemplateView, your project's views should inherit from core.views.TemplateView, and they'll automatically pass the grunt_js and grunt_css context variables to your templates.

Note that django.views.generic.base.ContextMixin is only available in Django 1.5+, so you'll need to make some adjustments for older versions of Django. The refactor of the generic view get_context_data() method into its own mixin base class was a great change, because it allows you to set up mixin classes like this that safely call super() and simply need to be inherited to override generic view functionality.

If you're not using class-based views, or you're not ready to commit to providing your own generic view class overrides, you can alternatively add "grunt.context_processors.grunt_config" to TEMPLATE_CONTEXT_PROCESSORS in your settings file. This will add the grunt_js and grunt_css boolean values to all of your templates whose views use a RequestContext.

For both the mixin and the context processor, you can nest the grunt_js and grunt_css context variables under a key in the context dictionary. For the mixin, pass a key argument to the grunt_config() call. For the context processor, add a GRUNT_TEMPLATE_CONTEXT_KEY to your settings file.

Notes and other considerations

I built this project to improve my workflow with Django and Grunt. I work in a *nix environment, so I didn't bother with trying to support other platforms.

The grunt management command checks for the availability of Grunt on the command line by running the *nix command which grunt. This will fail on other platforms. I wasn't satisfied with the various cross-platform hacks for this problem that I found with a cursory Web search, so this Django application is only useful on *nix platforms for now.

The management command API that django-grunt previously defined has been extracted and expanded into its own project, django-commando. If you need to write, override or sequence existing management commands, consider using django-commando.

This project currently lacks tests. I'm not feeling particularly motivated to figure out a decent way to test the management commands. The code is pretty straightforward and succinct, but it would be better with some test coverage.

When I have some more time, I'm hoping to add tools to seamlessly integrate Grunt unit testing tools with Django. However, my goal is to keep the project as generic as possible, so that it's not tied to any specific Grunt plugins.

Pull requests are always welcome if you have ideas to fix this project's shortcomings or add new, useful features. Happy coding!