Django 1.3 patterns for older django versions

February 19, 2011 Tags: django, python, django-community

The Django 1.3 niceties, only for 1.3? :-)

Django 1.3 is approaching fast, with tons of bugfixes but also some very interesting features. Among those, two are particularly interesting as I think they improve a lot the way we write our Django apps: class-based generic views and the new contrib.staticfiles app.

Class-based views

The benefits of class-based generic views are quite obvious: instead of wrapping an existing function-based generic view and/or passing it a countless number of argument, functionality can be easily extended using inheritance. Although the learning curve is steeper, it's totally worth learning. But see the docs for that, it's not the point of this post.

Let's say you've got this big project that runs on Django 1.2. You're in the process of upgrading it to 1.3 but you're not ready yet, and you want to use class-based views for new features? Meet django-cbv.

Django-cbv is a backport of the class-based views from django trunk that you can use with older django versions. As of writing this, you need to install it from my github fork since the package on pypi is missing an import (Update: fixed in django-cbv 0.1.5):

pip install django-cbv

Once installed, you just need to add to your MIDDLEWARE_CLASSES:

'cbv.middleware.DeferredRenderingMiddleware'

And then in your views code:

import cbv as generic

class SomeView(generic.TemplateView):
    template_name = 'some_template.html'

When you're ready to upgrade to Django 1.3, switch the import statement to:

from django.views import generic

Then change the middleware settings and you can drop the cbv package!

For reusable apps developers, django-cbv can be used to provide a fallback for older django versions with a simple conditional import.

The staticfiles app

The addition of a new contrib app for managing static files clarifies the shady area of media files in Django. Now, we have a clear separation between media files and static files.

  • Media files are user-uploaded files. Typically stuff you put in FileFields. They are stored in settings.MEDIA_ROOT and can be accessed under settings.MEDIA_URL.
  • Static files are, well, static files. CSS / JS files, images, static assets provided by your apps. They are collected in settings.STATIC_ROOT and can be accessed under settings.STATIC_URL. Read the staticfiles docs for more info, again and again.

So, how can we apply this pattern to Django 1.2 or 1.1? Luckily Jannis Leidel provides a standalone version of contrib.staticfiles, django-staticfiles.

Here again, pip-install it:

pip install django-staticfiles

A few settings changes need to be made:

import django

INSTALLED_APPS += ('staticfiles',)

TEMPLATE_CONTEXT_PROCESSORS += ('staticfiles.context_processors.static',)

STATIC_ROOT = os.path.join(PROJECT_PATH, 'static')
STATIC_URL = '/static/'
ADMIN_MEDIA_PREFIX = '/static/admin/'

STATICFILES_DIRS = (
    ('admin', os.path.join(os.path.dirname(django.__file__),
                           'contrib', 'admin', 'media')),
)

(When you upgrade to Django 1.3, just delete the django import and the ADMIN_MEDIA_PREFIX and STATICFILES_DIRS setting)

Now, instead of putting your static files in STATIC_ROOT, put them in project/app/static/app/. They will be automatically served by runserver, and when you deploy just run manage.py collectstatic to copy everything to STATIC_ROOT.

In your templates, you used to link to your static files like this:

{{ MEDIA_URL }}css/style.css

Instead, use STATIC_URL:

{{ STATIC_URL }}blog/css/style.css

There is still one area where Django will look for static files using MEDIA_URL: the forms' media handling system. To make it use STATIC_URL instead of MEDIA_URL, I haven't found a better solution than monkepatching. Put this piece of code somewhere in your project, in an app's __init__.py for instance:

from urlparse import urljoin

from django.forms import widgets
from django.conf import settings


class Media(widgets.Media):

    def absolute_path(self, path, prefix=None):
        if (path.startswith(u'http://')
            or path.startswith(u'https://')
            or path.startswith(u'/')):
            return path
        if prefix is None:
            if not hasattr(settings, 'STATIC_URL'):
                prefix = settings.MEDIA_URL
            else:
                prefix = settings.STATIC_URL
        return urljoin(prefix, path)

widgets.Media = Media

(Again, delete this snippet from your project when you upgrade to django 1.3)

With this snippet, all the files declared in your forms or widget's Media inner class will be resolved using STATIC_URL.

Staticfiles and class-based views are two really nice improvements and they deserve some love even with older django versions. I hope to see interesting uses of both, I already can't see myself going back to the "old" way anyway :).

Comments

February 19, 2011Aamir Maniar

This seems good, have you tested with other associated features like view decorators?

-
http://www.technobits.net - Always remain technically updated.

February 19, 2011Bruno

Aamir, the documentation explains well how to decorate class-based views:

http://docs.djangoproject.com/en/dev/topics/class-based-views/#decorating-class-based-views

The result of as_view() is a callable that can be decorated just like any function-based view, and the method_decorator helper was added in Django 1.2. With Django 1.1, decorating the dispatch() method with @login_required or @permission_required should just work since they have an "auto adapt" behaviour.

Add a comment

Comments are closed for this entry.