Django の汎用ビューの使い方を調べるために公式ドキュメントを読んだんですが、今更ながら Django1.3 で汎用ビューが関数からクラスに変わっていることを知りました。1.3 リリースは今年の3月23日だから、8か月近く知らなかったことになるかな。ハズカシイ。
関数版の汎用ビューもまだ使えるみたいですが、ドキュメントを見た感じたと、クラスベースの方が便利そう。
試しにいくつかのビューをクラスベースの汎用ビューを使って実装してみました。現在開発しているサービスのコードから抜粋。
from django.views.generic import ListView class UserPostListView(ListView): # ビューが描画に使うテンプレートを指定 template_name = "posts/post_user_list.html" # テンプレートで扱うときのクエリセットの名前を指定 context_object_name = "post_list" # 1ページに表示するのは20個まで paginate_by = 20 # ユーザーで絞り込んだクエリセットを返す def get_queryset(self): return Post.get_by_user(self.request.user) # テンプレートに渡すコンテキストにデータを追加 def get_context_data(self, **kwargs): ctx = super(UserPostListView, self).get_context_data(**kwargs) ctx.update({ "is_mypage": True, }) return ctx
ListView を使っています。たったこれだけでページネーションに対応したビューが実装できてしまいました。あとはテンプレート書くだけ。
登録用の CreateView も使ってみました。
from django.views.generic import CreateView class CreatePostView(CreateView): # ビューが描画に使うテンプレートを指定 template_name = "posts/post_new.html" # 登録に使うフォームクラスを指定 form_class = PostForm # 登録に成功したときのリダイレクト先を返す def get_success_url(self): return reverse("list_user_post") # ユーザーが入力したデータの検証に成功したら、 # ユーザーデータを追加して保存。 def form_valid(self, form): self.object = form.save(commit=False) self.object.user = self.request.user self.object.save() return redirect(self.get_success_url()) # テンプレートに渡すコンテキストにデータを追加 def get_context_data(self, **kwargs): ctx = super(CreatePostView, self).get_context_data(**kwargs) ctx.update({ "is_mypage": True, }) return ctx
POST かどうか判断する if 文が無くてスッキリ。個人的に、if 文で GET か POST か判断して処理を分岐するやり方は、どうもしっくりきませんでした。Python で最初に使ったフレームワークが AppEngine の webapp だったので、HTTP リクエストをクラスの get や post メソッドに割り当てるやり方のほうが好み。
クラスベースの新しい汎用ビューでは、内部処理をところどころフックできます。汎用ビューをベースに、テンプレートを指定したり、リダイレクト先を指定したり、コンテキストに値を追加したりするだけで、やりたいことが実現できてしまうことが結構あります。Django の汎用ビュー便利すぎ。
「主キーでモデルを取得してテンプレートに表示する」みたいな超単純なビューならともかく、ちょっと複雑になりそうなら、まず汎用ビューが使えるか検討したほうがいいですね。そして、もし汎用ビューが使えなかったら、仕方ないから自分でゴリゴリ書く、と。バグの少ない高品質なサービスを作る一番の方法は、できるだけコードを書かないことですから。