Django, Pluggable Apps, and Code Re-Use

by Jeremy Jones

Everything that I have built in Django so far has been pretty simple. Perhaps "limited scope" is a better choice of words. Each project I create tends to only contain one user-created app. There are a couple of sites that I'd like to build which could share some code between them. So, I decided to look into using multiple user-created apps and seeing how things fit together. My primary interests in this exercise were the url configuration and template inheritence among applications.

I created a project named "mproject" and, for the sake of simplicity, three apps named "main_app", "foo_app", and "bar_app" under the "mproject" directory. In addition to the autogenerated files, I created a "urls.py" for "foo_app" and "bar_app" and one html template for each of the three apps (named "bar_main.html", "foo_main.html", and "base.html"). Here is a tree view of the directory structure:


.
`-- mproject
|-- __init__.py
|-- bar_app
| |-- __init__.py
| |-- models.py
| |-- templates
| | `-- bar_main.html
| |-- urls.py
| `-- views.py
|-- foo_app
| |-- __init__.py
| |-- models.py
| |-- templates
| | `-- foo_main.html
| |-- urls.py
| `-- views.py
|-- main_app
| |-- __init__.py
| |-- models.py
| |-- templates
| | `-- base.html
| `-- views.py
|-- manage.py
|-- settings.py
`-- urls.py


I added the following three lines to the INSTALLED_APPS section of my settings.py file:

'mproject.main_app',
'mproject.foo_app',
'mproject.bar_app',

This is how applications are "plugged in" to your Django project.

I added the following to the project-level urls.py:

(r'^foo/', include('mproject.foo_app.urls')),
(r'^bar/', include('mproject.bar_app.urls')),

This says to use everything in the foo_app.urls url configuration, but map it by prepending "foo/" to all those entries. Likewise for bar_app.urls and "bar/".

main_app/views.py is empty. Since this is a simple example, the main application isn't doing anything.

main_app/templates/base.html:

<html>
<head>
<title>{% block title %}Main Base Title{% endblock %}</title>
</head>
<body>
{% block content %}
Unset Content
{% endblock %}
</body>
</html>


bar_app/urls.py:

urlpatterns = patterns('',
# Example:
# (r'^mproject/', include('mproject.apps.foo.urls.foo')),

# Uncomment this for admin:
# (r'^admin/', include('django.contrib.admin.urls')),
(r'^main/', 'mproject.bar_app.views.main'),
)

I only created one mapping for this app. "main/" will map to the function mproject.bar_app.views.main. But, since the main url config is including this url config, "/bar/main/" will map to the mproject.bar_app.views.main function.

bar_app/views.py:

from django.shortcuts import render_to_response

def main(request):
return render_to_response('bar_main.html', {})

This simply links the "main" function with the "bar_main.html" template.

bar_app/templates/bar_main.html:

{% extends "base.html" %}

{% block title %}Bar Title{% endblock %}

{% block content %}Bar Content{% endblock %}

Here, I'm extending base.html, which Django will pick up from the main_app application. I'm overriding the title and content blocks so that this template gets to fill in its own content.

Here are the details on foo_app, but without explanation since I'm doing the same thing here as in bar_app.

foo_app/urls.py:

urlpatterns = patterns('',
# Example:
# (r'^mproject/', include('mproject.apps.foo.urls.foo')),

# Uncomment this for admin:
# (r'^admin/', include('django.contrib.admin.urls')),
(r'^main/', 'mproject.foo_app.views.main'),
)


foo_app/views.py:

from django.shortcuts import render_to_response

def main(request):
return render_to_response('foo_main.html', {})



foo_app/templates/foo_main.html:

{% extends "base.html" %}

{% block title %}Foo Title{% endblock %}

{% block content %}Foo Content{% endblock %}



A couple of really practical uses for this come to mind. First, this is an easy way of separating logical pieces of your application. This approach makes it really easy to separate your site into its logical pieces. For example, you could have a blog, main static content, user reviews, photo gallery, etc. each in their own applications. This should lead to a less cluttered views.py file (even though you should be able to do something similar by separating things out and creating a "views" directory and putting logically separated code there).

Second, this makes for really nice sharing of applications among projects. Why not create your own customized user registration app and use it in each of your projects? Or a blog? Or photo gallery? (Or what have you.) All you have to do to re-use code is add an entry to the INSTALLED_APPS section of your settings.py file and create an include in your urls.py file.

I remember hearing or reading someone say that the pluggability of Django apps is one of its under-touted features. I couldn't agree more. Something that I had previously avoided because I didn't need it is turning out to be an incredibly powerful feature.

I've uploaded the project and app files here for anyone interested.

10 Comments

Horst Gutmann
2006-09-19 06:48:31
The ease of reusing components with Django is actually my main reason why I'm interested in it. A repository of some 3rd-party modules for websites would be really nice esp. for things like ... well, a simple bugtracker, FAQs etc. I wonder though, why the contrib.auth module doesn't have a register view :-)
Joshua Bloom
2006-09-19 10:07:38
Jeremy, regarding code reuse in Django, if you have a photo gallery mproject.gallery for instance located in /mproject/gallery could you use that same code (same files) in a separate project? IE otherProject/gallery or would you need to duplicate the files on disk?
Jeremy Jones
2006-09-19 10:36:43
Joshua -


My thoughts on code reuse were to create a repository of apps that you can just plug in. For example, for stuff that I work on, I'll probably create a "jeremymjones.apps" package and have something like jeremymjones/apps/gallery/work/ and jeremymjones/apps/gallery/release/ directories in an SVN repository. I'd then probably create a Makefile (or something comparable - looking at scons right now) for the project and tell it which revisions of which apps it should include. And when you build a release for a project, it'll pull down the versions of the apps you want. Anyway, I know that's clear as mud. We use something like that here at work. Maybe if I hammer out the process (especially using scons), I can blog about it or write an article or something.

Jeremy Jones
2006-09-19 10:37:27
Joshua -


And I should have said that after going through this whole procedure, you'd have a common name for any project you're working on. In this case, "jeremymjones.apps.gallery" would be the app.

Horst Gutmann
2006-09-19 10:53:02
@Joshua: From what I can tell, the app just has to be in your $PYTHONPATH with all it needs inside of its directory. Then you should be able to include your gallery in your app listing inside your projects settings.py. Haven't tried it yet, though, but since Django has some bundled apps that you don't have to copy into your project directory, I think it at least looks promising :-)
Vance Dubberly
2006-09-19 13:56:50
Ya breaking up modules into python packages is a pretty nice albeit this is hardly unique to django, both Rails and TG do it.


The fun part comes when you have a module that uses other app modules. If you were thinking that your package was going to be a stand alone app, you'll have alot of retooling to do. But that's an issue of the programmer not the framework.


Oh and django's template system is the best out there, by far! OMG

Michael Bernstein
2006-09-21 20:46:30
Jeremy, have you also looked at the kind of component reuse that's going on in Zope3? The whole stack is built to facilitate re-use, and not just the sort of coarse-grained 'glue apps together into a site' re-use you're demonstrating here (which Zope2 has had for years), but the ability to compose functionality and infrastructure from 3rd party components into new applications.


Philipp von Weiterhausen recently posted a good roundup of some of the components available for Zope3.

Jeremy Jones
2006-09-22 08:35:42
Hi Michael,


I tinkered around with Zope a number of years ago back when I was starting out with Python. I have really only glanced occasionally since then at the current state of things. I'll have to check it out.

None
2006-12-30 06:59:19
I see first time your site guys. I like you :)
Frank Tegtmeyer
2007-02-13 14:01:21
What I miss is something like the scope concept of skunkweb. Your example does highlight Djangos weakness in this area: you have to introduce a namespace concept into the template names which is odd at least. With a real scope concept you could use the same template names in all the applications.
The applications should be able to define their template paths relative to their "top directory" too. This would make them really pluggable.