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.
Comments: 16
I have a feeling there should be a comma: "In tests, for example".
Very cool, I hadn't realized django could be so compact. Coming from a rails standpoint, this reduces the number of files I'd expect in a micro-project by a ridiculous amount
This seems super cool and pretty much exactly something I was looking out for. As a novice beginner in Python and learning Django, I often wondered about the heavy boot-strapping Django did for small learning projects. This definitely helps my concern. Though, am still grasping many of the entities used in this post. It would be great help if you could wrap all this in a script and create a utility out of it for someone like me. Many thanks again for this post and explanation.
Thanks for showing this. I always had trouble understanding what Micro-frameworks are for if everything is doable with a stripped Django.
You can use models too.
Ah! long needed. Many thanks
Just for fun, I decided to see how easy it would be to factor what was left of the boilerplate in your single-file example into its own reusable chunk. It turned out to be pretty straightforward, and yielded something like this:
The source of the reusable chunk is here.
Andrew, this is totally cool! May be it's even getting close to be actually useful :-).
Yeah... I'm curious to see if it's possible to encapsulate the application state in an object, like Flask does, rather than having everything be global and having to hang all sorts of stuff like urlpatterns off of the main file. I think Simon Willison experimented with some of this a couple of years ago, so I might look into how he did it. I think getting models working would also be pretty cool (and I see that there are some tips on that in Luka's comment), because I don't think it would be that big of a stretch to be able to build single-file projects that leverage existing reusable apps, like the admin.
Nice post. I wish I could speak another language as well as you appear to speak English!
Some of this seems out of date, e.g., at least in Ubuntu it's now just django-admin instead of django-admin.py, and the 3-file version doesn't run out of the box (apologies, I saw the problem on someone else's machine and don't have the errors to hand).
It'd be great if you could fix it up somehow for the next gullible fool who says "just search for 'minimal django example' and run what you see"
This post is more than three years old, of course it's out of date :-)
oh, amazing! what a long time ago this was obvious for some clever people :)))
@Luka Zakrajsek suggested model connection. A little less hacky solution could be as follows: