June 12, 2013
by Dan Poirier
0 comments
Categories:
Technical
Tags:
django, python

Making your Django app more pluggable

This blog post is about ways of doing things in your Django app, and pitfalls to avoid, so your app is easier to plug in to other sites.

Introduction

One of the most enticing promises of Django is that you’ll be able to add features to your site by just downloading apps and plugging them in. That’s one of the many reasons we use Django here at Caktus: we can build useful web sites for our clients more quickly by not having to re-invent the same building blocks all the time, and focusing on the unique value-add for each client.

This is also one of the attractions of building and releasing open-source Django apps: they’re easy for other people to use, and having other people use your code is very satisfying.

But Django apps don’t become easily pluggable automatically, and it’s easy to do things in a way that make it much harder for other sites to use an app.

A book could be written about this topic. This post just gives examples of some areas that can cause problems, and might stimulate some thought when designing your app.

Not everything needs to be an app

Does your package have to be an app at all?  If a package can provide useful features without being added to INSTALLED_APPS, that’s the way to go. There are some things that Django only looks for in apps, like models and custom template tags. If those are needed, you’ll have to make it an app.

Features of highly pluggable apps

  1. Can be installed anywhere on the Python path, ideally just using “pip install package-name”.
  2. Does not require the site to have any explicit knowledge of what directory the app ended up in. (It’s not necessary to add the app’s install directory to a setting or anything like that).
  3. Installing and configuring the app in a site does not break any existing function of the site.
  4. The app can be upgraded by just installing the newer version, and possibly running migrations.  (The site might have to make changes to take advantage of new features, of course, but an ideal app adds features without changing the behavior of existing features, apart from bug fixes.)
  5. As a corollary to some of the previous features, installing or upgrading the app doesn’t require copying files or sections of code from the app or its documentation into the site.
  6. Can be customized without forking the code.

Assumptions that can be made about the site

It’s best if the app can avoid assumptions about how the site does things. If it does matter, assume the site does things in the most straightforward, default way. Those who have customized their site behavior away from how Django does things out of the box presumably have the know-how to cope, or have accepted that they won’t be able to use some pluggable apps without pain.

For example, if the app provides static files, assume the site uses the Django staticfiles app, and provide the static files in a way that simply installing the app will make the static files available through that app.

Templates

Template tags are a good way to provide enhanced features that a site can use in its templates. If a template tag just provides some data, and the site can embed it, lay it out, and style it however it wants, that makes it very easy to use.

If a template tag really needs to return its data with markup to be useful, then consider using a small template to format the data, and documenting it. The site can provide an overriding template to change the formatting without having to change your app.

If your app serves whole pages that need templates, then provide basic templates that inherit from ‘base.html’ and put the content in a ‘content’ block. Most sites have a base.html template that provides the basic page framework and styling around a content block anyway, or can easily add one. And a site can always copy and override the app’s example templates a lot more easily than writing templates from scratch.

Models

When designing the models for your app, keep in mind that you can use Generic Foreign Keys to link to any model.  The comments app that comes with Django provides a good example.

Provide South migrations for your models, and make sure as your app evolves, the migrations still work to get from your initial state to your latest release. That helps to make upgrading your app as painless as possible.

Settings

If possible, any settings you define for your app should not be required, and should have reasonable defaults.

If a setting is required and cannot have any reasonable default, then check for it and give a useful error message if the user has forgotten to define it.

Since Django’s settings occupy a single namespace, it’s a good idea for any new settings required by your app to start with the app name.  APPNAME_TIMEOUT is much less likely to conflict with some other app’s setting than TIMEOUT.

Some apps reduce the number of names they add to the Django settings by just using a single setting whose value is a dictionary with all the more specific settings. E.g.

APPNAME_SETTINGS = {
        ‘TIMEOUT’: 29,
        ‘PATH’: ‘/foo/bar’,

}

I’m not sure if that’s a good idea or not, but it’s something to consider.

Dependencies

If your app has dependencies, they should be configured in setup.py so that when a site “pip installs” your app, its dependencies will be installed automatically.

Configure minimum versions of the packages your app depends on, but try not to pin them to a specific version. If your app requires “foobar==1.0.3” and another app the site uses requires “foobar==1.0.4”, that’s going to cause a headache for the user.  If your app requires “foobar>=1.0.3”, there’s no problem.

If you want to limit the versions, you can use a specification like “foobar>=1.0.3,<2.0” so that if the foobar package releases a 2.0 version, your app won’t try to use it until you’ve had a chance to test with it and release an update.

Further reading

The Django 1.5 documentation has a tutorial on Reusable Apps. It focuses more on packaging than design, so it’s a good complement to this post.

Daniel Greenfeld and Audrey Roy’s book Two Scoops of Django: Best Practices for Django 1.5 has a section What Makes a Good Django Package? with good hints on making a useful, reusable app. It’s adapted from their DjangoCon 2011 talk, “Django Package Thunderdome: Is Your Package Worthy?” so you can get some of the ideas from those slides (but the book has a lot of other useful things in it, I recommend it as a whole).

James Bennett’s Practical Django Projects has a chapter on writing reusable Django applications. It discusses the philosophy of what makes a good reusable app, and then gives some examples of handling specific coding issues of plugging in apps, at much greater length that we can here.

blog comments powered by Disqus