Saturday, July 25, 2009

[geek] Splitting django views

Geekiness ahead; you have been warned.

Steve wrote a post about splitting django views. Consider this as another viewpoint / way of doing the same.

The primary problem is to split django's views.py into multiple different modules, ostensibly to make them smaller and more modular (duh).

To begin with, I'm not entirely certain the problem exists - in MVC or MD paradigms, the view is typically very tiny and almost entirely devoid of logic. It is there only to reflect changes made to the model. Consequently, it is unclear that the views.py needs splitting at all. I have not done enough large scale django development (my projects have been of the tiny homebrew variety), so lets assume that views become unmanageable in the long run and need to be split up.

Lets say you have a setup and have the following directory structure:

> ls
__init__.py manage.py* settings.py urls.py urls.py.orig views.py

and views.py looks like this:

> cat views.py

from django.http import HttpResponse

def index(request):
return HttpResponse('index')

def foo(request):
return HttpResponse('foo')

def bar(request):
return HttpResponse('bar')

Now we'd like to break apart views into a directory, so we change it to:
> ls -R
.:
__init__.py manage.py* settings.py urls.py urls.py.orig views/

./views:
bar.py foo.py index.py

where each file in the views directory has its own little function:

> cat views/foo.py

from django.http import HttpResponse

def foo(request):
return HttpResponse('foo')

At this point, note that there is no __init__.py at all.

The original url resolver obviously will not work because it looks like this:
urlpatterns = patterns('',
(r'^foo/', 'views.foo'),
(r'^bar/', 'views.bar'),
(r'^$', 'views.index'),

So we change that too:
urlpatterns = patterns('',
(r'^foo/', 'views.foo.foo'),
(r'^bar/', 'views.bar.bar'),
(r'^$', 'views.index.index'),

and the site is back up as normal.

I think this approach - explicit directions in urls.py is much cleaner and much more inkeeping with django's spirit of avoiding magic. Note that the other approach which basically imported all functions the minute __init__.py was touched would maintain the urls.py mappings, thus saving a bit of work. However, I think it would confuse the heck out of people, not to mention result in wierd name collisions if any of the individual view files happen to export the same function.

2 comments:

Steve Lacy said...

Yeah, this is actually where I started, but I'm *really* disappointed by Python making me say "project.app.views.foo.foo" It just feels *so* wrong to me, like there must be some better way, which is where I started saying to myself "there must be a better way".

Anyway, yeah, hacking __init__.py seems horrible. I think this is what they do for the Google AppEngine version of Django, but I couldn't find a copy of that (although I didn't look very hard).

Leo said...

Why wouldn't you just put an __init__.py in /views?

'app.views' is just a python package.