How I Write Django Reusable Apps

Over the past year I have written quite a number of reusable apps or have made significant contributions to previously existing ones (most if not all of these are under Eldarion or Pinax organizations on GitHub). A sort of a pattern has developed as I have learned little bits from trial and error so I thought I'd capture them in a single post as they might be useful for other app authors.

In summary they are:

  • Start with a Real Project
  • Provide Signals
  • Properly Package and Document Your App
  • Maintain a CHANGELOG
  • Provide injection points in Views

Start with a Real Project

I find it very difficult to develop good useful apps with features that are meaningful and work well, when built in the abstract. I also need a real project (whether an active project under development or a sample project doing real work for the purpose of proving out said app). All of the apps I have written to date started in a project being built for real. I got it working in the project, abstracted away domain specific and project specific bits and replaced the in project code with a requirements listing for the packaged version of the code.

This is one area that Pinax helps tremendously. Encouraging app development through the project structure of apps living in their own apps package helps to remind me to keep things isolated from the get-go.

Provide Signals

At first I didn't think much about signals as I thought the site developer could always register receivers for the model signals (e.g. post_save). However, I started running into situations where I wanted to know things were happening at the user interaction level which occurs in the views. Most importantly, I wanted access to the session when some event was occurring. Therefore, I started making a habit out of adding signals for the different sources of user interaction with the app and sending them in the views. And in doing so one of the arguments I started providing is the request object. By providing the request object, you allow the site developer to add their own language using apps like django.contrib.messages framework.

Properly Package and Document Your App

If you app isn't packaged it's going to be very hard to publish and if it's not published (on PyPI), it's going to be difficult for users to find and install it. Likewise, if you app isn't documented, then users are going to have to crawl through your code for reference instead of just glancing through some well-written documentation.

In order to speed the process along of releasing new apps, I started a repo that I base all new apps on. It includes a setup.py and a docs/ directory with a skeleton set of docs. Just copy these bits into a new folder and add your app code. Modify the variables in setup.py and the docs/Makefile; write the documentation appropriate for your app, and it's really that easy.

I then go to ReadTheDocs, create a project, go to GitHub and add a post receive hook for ReadTheDocs so that docs get rebuilt on every commit. Lastly, I build and upload an sdist to PyPI.

Maintain a CHANGELOG

Part of the docs is a changelog.rst whereby I maitain a list of changes for each versioned release. This is a good place to call out backwards incompatible changes and even provide example schema migrations (I disagree with putting South migrations in resuable apps).

Provide injection points in Views

If your apps has views, think through various ways of extending them. If using functional based views you can have them take a form class for instance to use instead of a default form. Then you can pass this in at the site level in the urls.py definition. If using class-based views you can provide even greater extensibility as a site developer can just subclass your view and override methods you provide as extension points. Another way of providing extensibility with class-based views is to allow mixins to be defined in settings.py that your app will mixin to override or augment built in functionality.