クラスベースのビュー

社内チャットで出た質問。GETパラメータによって呼び出すビューを変えたい場合はどう書くとよいか。
クラスベースのビューを書くと割ときれいに書けるんじゃないかな、という話。
まずは素直に書いてみる。

def index(request):
    action = request.GET.get('action', '')
    if action == '':
        return view_default(request)
    elif action == 'abc':
        return view_abc(request)
    raise Http404

まあ、1回しか使わなくて特に変更も多くない、呼び出すビューの数も少ないというならこれでも良いのだけど、たとえば呼び出すビュー50個とか100個になってくるとメンテするのが大変になってくる。
djangoのviewはcallableであればよいので、クラスベースのビューを作って、そこにカスタムのビューを登録していく、という風にしてみる。

core/views.py

# coding:utf8
from django.http import Http404

class ClassBaseViews(object):
    """
    クラスベースのビュー
    以下のようなビューを簡単に作れます。
    http://example.com/?action=アクション名
    挙動をいろいろ変えたい場合は、ClassBaseViewsを
    継承して__call__などをオーバーライドするとよいかも。
    """
    def __init__(self):
        self.views = {}

    def __call__(self, request, *args, **kwargs):
        action = request.GET.get('action', '')
        if action in self.views:
            return self.views[action](request, *args, **kwargs)
        raise Http404

    def register(self, view, name=''):
        self.views[name] = view

base_views = ClassBaseViews()

こうするとbase_views(request)のように使えるので、urls.pyは次のように書ける。

urls.py

urlpatterns = patterns('',
    (r'^$', 'core.views.base_views'),
)

あとはbase_views.registerを使ってビュー関数を登録すればよいのだが、楽をするためにdjango adminのautodiscoverのようなものを実装してみたりした。詳細はソース参照。
登録する場合は各アプリケーションディレクトリにaction_view.pyを作って、その中でbase_views.registerを呼ぶ。

from django.http import HttpResponse

from core import base_views

def hello_view(request):
    return HttpResponse('Hello world!')

base_views.register(hello_view)

上の例の場合、registerでnameを省略しているため、 http://example.com/ もしくは http://example.com/?action= でアクセスできることになる。

from django.views.generic.simple import direct_to_template

from core import base_views

from app2.forms import MyForm

def show_form(request):
    return direct_to_template(request, 'app2/show_form.html', {'form': MyForm()})

base_views.register(show_form, 'form1')

この場合は、 http://example.com/?action=form1 となる。
クラスベースのビューは再利用、拡張をしやすいし、アプリを分割する上でも使えるので便利。