One of the great strengths of Django is the vast ecosystem of reusable apps. People saw common patterns in what they were making and abstracted those out in to an app that anyone else can use in their own projects. The Pinax project, in which I am a committer, is attempting to take the ecosystem and bundle up these reusable apps into more of a deliverable. We are working on having different flavors of Pinax that will suit what you are trying to do. This also doesn't mean you couldn't pick and choose your own apps.
I want to take the time in this post to go over conventions that should be established for reusable apps and Django apps in general.
Reusable vs Non-Reusable Apps
A reusable app is meant to do one thing and do it well. The second it cross the boundary of doing more than one thing you need to question yourself whether or not what you are adding is appropriate. Should that one new feature really be contained in its own app to flourish and grow on its own?
A Story
A good case of where I really began to question myself was during the development of django-categories. I started the project a little over a year ago. This was literally just before Jonathan Buchanan started to take some ideas on the Django wiki and dump them into what we now know as django-mptt. At the time I was just learning about MPTT and how it can benefit me in storing trees in a relational database. I was tasked with the need to create categories at work so it felt like the right thing to use.
I started to write django-categories in a reusable way. I used a parent foreign key to deal with the hierarchy. It quickly became apparent that the data set I had was a bit too large for some of queries I was doing using a simple parent foreign key. I started to re-work django-categories to include the denormalization that MPTT provides. Right then django-mptt came out. I was simply attonished by how well implemented it was. It stayed out of your way.
At this point I had two choices. One, use django-mptt having a dependancy on another reusable app. Two, continue on having on my own implementation of MPTT that was really lacking. I can't say for sure that this is a case of doing more than one thing, but it certainly broad sided me (in a good way) in understanding where lines can be drawn.
I ultimately just closed the project due to my particular use case and
pointed people to django-mptt. However, recently I have come across a few
cases where having an app that provided a Category model, a URLconf, views
and some feeds around generically relating objects to a tree is a good idea.
I have since reopened the project and moving forward with it.
Drawing the lines
The case I show above was specific to my needs, these things need to be thought of on your own. If you are working on a project using Django what is out there? Does it do what I need? It is always best to just start looking.
You may already have reasons for not making an app resuable. In the Pinax project we have a few of those cases. We have three levels of apps.
External apps
Third party apps provided by the community. They are svn:external'ed in
the repository.
Local apps
These are apps that we have written to fill a specific feature on our site. They are ones that can be written with many assumptions but the ideas and features they imploy can be made reusable in the future.
Project specific apps
These are esstenially "core" apps. They live at the project level. Currently,
that is either in the basic_project or complete_project. These
have dependancies in such way that can not make them resuable in a generic
sense. They are glue between external and local apps to make up certain
features.
This is ultimately going to be a decision you will need to make. I want to clarify that a resuable app does not mean it is open sourced and freely available. They are primarily a clean implementation and proper separation in your Django project.
Make an app
Creating an app in Django is simple:
manage.py startapp appname
This will give you a skeleton app that you then go and code. One thing that is
left to be desired are many of the modules most apps you see have. For
example some apps come with forms that you can use in your views or templates.
Many people get confused on where these things should go. Some argue that
Django provides a horrible starting point. There are many things that should
be there, but isn't. This has been argued for/against in the past. I think the
starting point Django provides you is an excellent balance between giving you
everything under the sun that you might use and nothing at all. I am certainly
open to improving startapp, but the default behavior is good enough. The
documentation makes up for the rest. You are reading it right?
Models
Models are the foundation around accessing your data in your database. Django
will create you a models.py file for you. This is where models go.
One thing that some people don't know is models can be seperated out. If you make
a models module (a models directory with an __init__.py in it) you
can create many other Python modules with models that belong to that app. You
must specify the app name in app_label of the Meta class in all the
models for this to work. I typically recommend against this. The first reason
is because of the idea behind a resuable app I discussed above. If you have
this many models in one app, you doing something wrong. Another issue is you
begin to have too many interdepent models that end up causing circular imports.
Forms
Forms are commonly placed in a forms.py module. This includes regular
forms and model forms. Putting model forms in your models.py is just the
wrong placement. They are just Python classes that could live anywhere
though, but that would defeat the purpose of this article.
Admin Inteface
The admin interface is not enabled by default. However, many people use it.
It went through a complete refactor during its newforms-admin days. Originally
the only place where you specified the admin bits was in your models. NFA
fixed that by requiring you to put the new ModelAdmin declarations in an
admin.py module.
Note
People who used the newforms-admin branch had to deal with many issues
with the placement of ModelAdmin classes. It mostly came down to
where the register calls were happening.
If you are defining ModelAdmin and making admin.site.register
calls in your models.py or even explicitly importing an admin.py
in the app's __init__.py stop doing that. Now.
The admin app comes with a handy function that will properly import all of
your admin.py modules for you. It is called autodiscover. I have gone
into depth about this in my newforms-admin migration post.
Views
Views are what deal with the request once it is routed. Django provides you
with a views.py module where you have your view functions. One thing to
note is that these are simply callables. What I mean in the general sense is
that you are not tied to using a Python function. One thing that the admin
does is it uses a class that represents many methods (aka views) that deal
with the HttpRequest. This is commonly refered to as class-based views.
Here is a quick example:
class MyView(object):
def __call__(self, request):
return HttpResponse(self.get_text())
def get_text(self):
return "hello world"
The above is a horrible view, but a good example. In a resuable app you can easily provide overridable behavior for users of the views. There is proposal going around in the Django world about using this approach for generic views. I am personally a big +1 to that idea. It really stemmed out of the many use cases we saw during the newforms-admin period. Views would become their own classes where they can inherit base functionality and everything is overriable.
Templates
This deserves its own post. I have one in the works about I how feel in this area and what is acceptable in a reusable app.
Template tags
Template tags are awesome. They already have a pretty well established convention due to the fact its structure is well defined:
my_super_sweet_app/
__init__.py
models.py
templatetags/
__init__.py
my_super_sweet_app_tags.py
my_super_sweet_app/templatetags/my_super_sweet_app_tags.py is where you
define your template tags. Be it inclusion tags, filters, custom tags, etc...
One thing that I have not seen addressed well enough is custom template tag syntax. There is sort of convention, but nothing very well established. Let me describe how I typically handle this:
{% images_for_object [obj] as [varname] %}
The structure is that you pluralize what the model is. In this case I have an
Image model and it contains a generic foreign key. This template tag
takes in an object and then builds a new context variable for you containing
the data you are looking for.
The convention I am trying to establish is that template tags read nicely. The above tag uses the word 'as' to indicate that the left hand side is going to be stored in its right hand right. There are other cases where other keywords can be used to show how a template tag works:
{% latest_flights 5 for profile.user as flights %}
In the tag above we are grabbing 5 latest flights for a given user and
storing them in the context variable flights. I am not saying we can make
a good convention around every template tag there is, but certainly I wanted
to bring it up so you too can think about how this might be good in some
places of your own template tags.
You are cleared for landing
This blog post has been mostly a brain dump of many things I see through out my apps. I encourage you to implement some of these ideas and take others opinions into consideration. The Django reusable app ecosystem is going to continually get stronger. There are a couple of things I have left off this post only because I am just beginning to learn more about them myself. I will be certain to post more things about reusable apps in the near future.
