Django as a micro-framework

There is a common knowledge that Django is not suitable for writing small ad-hoc projects that don't need anything special apart from displaying some HTML. Boot-strapping a Django project is considered a work substantial enough to not bother with it just for a couple of functions.

It was always "obvious" to me that it's not the case. But recently, due to some phrase I heard from a colleague, I realized that it's not obvious to everyone. Because many people just don't think about it all the time. Well, you know how it happens… So I'd like to show how to create a Django project with minimal hassle.

Simplification

Don't use the startproject command. It creates a nice project base with an annotated settings file but if it's not your first time looking at Django then you can do without it. The manage.py file is also not needed since it's just a local "helper" for django-admin.py. Don't consider the project a container for applications and don't write your code as a pluggable application. Applications are one of the most powerful architectural features of Django but for small tasks you just won't need all this flexibility.

After that we'll have something like this:

settings.py: DEBUG = True ROOT_URLCONF = 'urls' TEMPLATE_DIRS = ['.']

urls.py: from django.conf.urls.defaults import * import views urlpatterns = patterns('', (r'^$', views.index), (r'^test/(\d+)/$', views.test), )

views.py: from django.shortcuts import render def index(request): return render(request, 'index.html', {}) def test(request, id): return render(request, 'test.html', {'id': id})

This is a fully functional Django project. You can run it with this command:

django-admin.py --settings=settings runserver

What you get is a Django request-response pipeline, template system. Most nice things like form library should also work though I didn't check. What you lose is the ORM and all applications depending on it. But hey, it's for a "micro" task after all!

Extreme

I wanted to push the idea a bit further to see the moment when it becomes absurd and decided to put everything into one runnable module. And I succeeded:

web.py: #### Setup from django.conf import settings if not settings.configured: settings.configure( DEBUG = True, ROOT_URLCONF = 'web', TEMPLATE_DIRS = ['.'], ) from django.conf.urls.defaults import patterns urlpatterns = patterns('', (r'^$', 'web.index'), (r'^test/(\d+)/$', 'web.test'), ) #### Handlers from django.shortcuts import render def index(request): return render(request, 'index.html', {}) def test(request, id): return render(request, 'test.html', {'id': id}) #### Running if __name__ == '__main__': from django.core.management import execute_from_command_line execute_from_command_line()

You can run the file like this:

python web.py runserver

However putting it into one file didn't yield much profit. For the file to be readable it has to be separated into named sections. This kind of hints that they really should be separate files after all. Plus, I wouldn't dare to write all this code from memory because of all the boilerplate in it: remember to avoid double initialization of settings, remember long imports etc.

But still it's funny that it worked out :-).

Afterthought

When messing with project initialization I've noticed that ROOT_URLCONF setting is used inside the core of request processing. This binds Django pipeline to the existence of a real module with the defined urlpatterns variable. I think Django would be more flexible if it was possible to set URL configuration programmatically with a simple list:

settings.configure( DEBUG = True, urlconf = [ (r'^$', index), ], )

It might be even useful in practice in cases where you need to setup an environment on the fly. In tests for example.