[Django]as_view()のメモ

#[Django]as_view()のメモ

初学者の学習メモ記事になります。
Djangoの汎用ビューで使われるas_view()がどのように動くかをメモ。
ここを理解できれば、汎用ビューのベースとなるViewを理解し、実装できるかなと思います。


##as_view()の使用例
Djangoでurls.pyファイルへルーティングを書く際に、汎用ビューを利用する場合に記載します。
以下が使用例です。

```urls.py
from django.urls import path
from . import views

app_name = 'recipe'

urlpatterns = [
    path('', views.[クラス名].as_view(), name='index'),
]
```


```views.py
from django.views.generic import TemplateView

class RecipeListView(TemplateView):
    """
    テンプレートビュークラス
    """
    template_name = '[アプリケーション名]/index.html'
```

この実装によって、ルートパスでアクセスしたページにindex.htmlの内容が表示されます。


##as_view()って何している？

上記の実装では「TemplateView」を継承し汎用ビューを実装していましたが、「TemplateView」は「View」を継承しています。
以下が「View」のソースです。(説明部分のみ抜粋)

```django/django/views/generic/base.py
class View:
    """
    Intentionally simple parent class for all views. Only implements
    dispatch-by-method and simple sanity checking.
    """

    http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']

    def __init__(self, **kwargs):
        """
        Constructor. Called in the URLconf; can contain helpful extra
        keyword arguments, and other things.
        """
        # Go through keyword arguments, and either save their values to our
        # instance, or raise an error.
        for key, value in kwargs.items():
            setattr(self, key, value)

    @classonlymethod
    def as_view(cls, **initkwargs):
        """Main entry point for a request-response process."""
        for key in initkwargs:
            if key in cls.http_method_names:
                raise TypeError(
                    'The method name %s is not accepted as a keyword argument '
                    'to %s().' % (key, cls.__name__)
                )
            if not hasattr(cls, key):
                raise TypeError("%s() received an invalid keyword %r. as_view "
                                "only accepts arguments that are already "
                                "attributes of the class." % (cls.__name__, key))

        def view(request, *args, **kwargs):
            self = cls(**initkwargs)
            self.setup(request, *args, **kwargs)
            if not hasattr(self, 'request'):
                raise AttributeError(
                    "%s instance has no 'request' attribute. Did you override "
                    "setup() and forget to call super()?" % cls.__name__
                )
            return self.dispatch(request, *args, **kwargs)
        view.view_class = cls
        view.view_initkwargs = initkwargs

        # take name and docstring from class
        update_wrapper(view, cls, updated=())

        # and possible attributes set by decorators
        # like csrf_exempt from dispatch
        update_wrapper(view, cls.dispatch, assigned=())
        return view
            :
            :
    def dispatch(self, request, *args, **kwargs):
        # Try to dispatch to the right method; if a method doesn't exist,
        # defer to the error handler. Also defer to the error handler if the
        # request method isn't on the approved list.
        if request.method.lower() in self.http_method_names:
            handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
        else:
            handler = self.http_method_not_allowed
        return handler(request, *args, **kwargs)

            :
            :
```

as_view()はdippatchメソッドを戻り値として返している。
dippatchメソッドは、http_method_namesで定義されたメソッド（getやpost）を呼び出すように実装されている。
なので、as_view()は、クライアントから発火したリクエストに対して、汎用ビューのget()やpost()を呼び出す。

ちなみに「TemplateView」のget()は以下の内容になっており、戻り値としてrender_to_responseを返している。

```django/django/views/generic/base.py
class TemplateView(TemplateResponseMixin, ContextMixin, View):
    """Render a template."""
    def get(self, request, *args, **kwargs):
        # RemovedInDjango40Warning: when the deprecation ends, replace with:
        #   context = self.get_context_data()
        context_kwargs = _wrap_url_kwargs_with_deprecation_warning(kwargs)
        context = self.get_context_data(**context_kwargs)
        return self.render_to_response(context)
```

以上。


##参考
[Django汎用クラスビューの実装を理解する](https://qiita.com/shinno21/items/831811c5b6fae31f7c99)
[Django base.py](https://github.com/django/django/blob/master/django/views/generic/base.py)