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:
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.
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.
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.
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).
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.