oebfare

Reusable App Conventions

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.

Entry Details

Published: Nov 4, 2008 at 10:04 PM

© 2007 - 2008 Brian Rosner