Help us understand the problem. What is going on with this article?

Djangoのクラスベースビューのas_viewて何なの?

More than 3 years have passed since last update.

ハッピーメリークリスマス。 この記事はDjangoアドベントカレンダーの18日目の代打です。

概要

  • Djangoには関数ベースのビューと、クラスベースのビューの2種類がある
  • クラスベースの方は利用する時に as_view というクラスメソッドからviewオブジェクトを生成している
  • これは一体何してるんですか? というメモを雑に残す。

前提

  • Djangoのビューはドキュメントにも書いてある通り以下の条件を満たす必要がある

    • requestオブジェクトを(第一引数として)受け取る
    • callableである
    • responseオブジェクトを返す
A view is a callable which takes a request and returns a response.

refs https://docs.djangoproject.com/en/2.0/topics/class-based-views/
  • Django 1.3 以前は関数ベースで書くのが普通だったが、なるべくコードを書かずに再利用可能なビューを作るためにクラスベースのビューが導入された。

as_view クラスメソッド

例えば以下のような関数ベースビューがあるとする

from django.http import HttpResponse, HttpResponseNotAllowed

def myview(request):
    if request.method == 'GET':
      return HttpResponse('Hello, World!')
    return HttpResponseNotAllowed('GET only accept')

これと等価なクラスベースビューを作ると下記のようになる

from django.http import HttpResponse
from django.views import View

class MyView(View):

    def get(self, request, *args, **kwargs):
        return HttpResponse('Hello, World!')

myview = MyView.as_view()
  • この myview = MyView.as_view()myview は実態は関数である。
  • as_viewDjangoのビューの条件を満たす関数 を作り出しているだけである

as_view を使わずにクラスベースビューを使う

たとえば as_view を使わずにクラスベースビューを使おうとすると下記のようになる。(この例自体に意味はない。)

from django.http import HttpResponse
from django.views import View

class MyView(View):

    def get(self, request, *args, **kwargs):
        return HttpResponse('Hello, World!')

def myview(request, *args, **kwargs):
    view_obj = MyView()
    view_obj.request = request
    view_obj.args = args
    view_obj.kwargs = kwargs
    return view_obj.dispatch(request, *args, **kwargs)
  • クラスベースビューは最初の起点が dispatch メソッドとなる
  • dispatch メソッド は リクエストメソッド を判定して、その リクエストメソッド と同じ名前のメソッドが実装されていたらそれを実行する。
  • つまり request.method == 'GET' だったら Myview.get を実行するということ
  • dispatch が実装されてるメソッドに応じて処理を自動的に分岐してくれるので、関数ベースでやっていた時のように自分で分岐を書かなくても良い。
# こんな感じで分岐を書かなくても良いという話

def myview(request):
    if request.method == 'GET':
      # code for GET

    if request.method == 'POST':
      # code for POST

    if request.method in ('PATCH', 'PUT'):
      # code for PATCH or PUT

    if request.method == 'DELETE':
      # code for DELETE

    return HttpResponseNotAllowed('Not accept method {}'.format(request.method))

Djangoの該当コード

実際にDjangoがview関数を生成している部分をgithubのコードから抜粋して確認する

# refs https://github.com/django/django/blob/master/django/views/generic/base.py#L49

@classonlymethod
def as_view(cls, **initkwargs):

   # 〜 省略 〜

   # 自分自身をインスタンス化するビュー関数を生成してreturn してる
   def view(request, *args, **kwargs):
       self = cls(**initkwargs)
       if hasattr(self, 'get') and not hasattr(self, 'head'):
           self.head = self.get
       self.request = request
       self.args = args
       self.kwargs = kwargs
       return self.dispatch(request, *args, **kwargs)

   # 〜 省略 〜

   return view
  • as_view は 自ら(=cls) をインスタンス化して dispatchメソッドを実行するような関数(=view)を生成して return しているのがわかる。

まとめ

  • クラスベースビューのas_viewは Djangoのビューの条件を満たす関数を生成しているだけ。極論を言えば条件満たせばなんでもいい。
  • クラスベースビューの基本的な機能は、リクエストメソッドに応じた処理の自動分岐を提供することである
  • それをベースにテンプレートをレンダリングする機能を加えたり、CRUDの機能が追加されたのが他のviews.genrice

    • 例) TemplateView, RedirectView, CreateView, etc...

以上おわり

tell-k
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away