0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

DjangoのLoginViewについて理解する

Last updated at Posted at 2023-11-12

初めに

LoginViewは、ユーザー認証とログインプロセスを容易に実装するために設計されたクラスです。

全体コードはこちら

LoginViewの継承クラス

RedirectURLMixinクラスとFormViewクラスを継承しており、ログイン成功時のリダイレクトロジックとフォームの処理をそれぞれ担当します。

LoginViewクラス

LoginView
class LoginView(RedirectURLMixin, FormView):

    form_class = AuthenticationForm
    authentication_form = None
    template_name = "registration/login.html"
    redirect_authenticated_user = False
    extra_context = None

    @method_decorator(sensitive_post_parameters())
    @method_decorator(csrf_protect)
    @method_decorator(never_cache)
    def dispatch(self, request, *args, **kwargs):
        if self.redirect_authenticated_user and self.request.user.is_authenticated:
            redirect_to = self.get_success_url()
            if redirect_to == self.request.path:
                raise ValueError(
                    "Redirection loop for authenticated user detected. Check that "
                    "your LOGIN_REDIRECT_URL doesn't point to a login page."
                )
            return HttpResponseRedirect(redirect_to)
        return super().dispatch(request, *args, **kwargs)

    def get_default_redirect_url(self):
        if self.next_page:
            return resolve_url(self.next_page)
        else:
            return resolve_url(settings.LOGIN_REDIRECT_URL)

    def get_form_class(self):
        return self.authentication_form or self.form_class

    def get_form_kwargs(self):
        kwargs = super().get_form_kwargs()
        kwargs["request"] = self.request
        return kwargs

    def form_valid(self, form):
        auth_login(self.request, form.get_user())
        return HttpResponseRedirect(self.get_success_url())

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        current_site = get_current_site(self.request)
        context.update(
            {
                self.redirect_field_name: self.get_redirect_url(),
                "site": current_site,
                "site_name": current_site.name,
                **(self.extra_context or {}),
            }
        )
        return context
属性 説明
form_class 使用されるフォームクラス。デフォルトではAuthenticationFormが使用される。
authentication_form カスタム認証フォームを指定する場合に使用する
template_name ログインページのテンプレート名を指定する
redirect_authenticated_user 認証済みユーザーがログインページにアクセスした際にリダイレクトするかどうかを制御する
extra_context テンプレートに追加のコンテキストを提供するために使用する

dispatchメソッド

基底クラスであるViewクラスのメソッドをオーバーライドしています。
このメソッドはリクエストされたHTTPメソッドに対応するビューメソッドを呼び出します。
詳しい説明はこちらをご覧ください。

このメソッドでは、redirect_authenticated_userTrueであり、ユーザーが既に認証されている場合にリダイレクト処理を行います。
リダイレクト先はRedirectURLMixinget_success_urlメソッドを使用して取得します。

get_default_redirect_urlメソッド

このメソッドは、ログインに成功した後のデフォルトのリダイレクトURLを返します。
next_page属性が設定されている場合はそれを使用し、そうでない場合はsettings.LOGIN_REDIRECT_URLを使用してリダイレクト先のURLを決定します。

get_form_classメソッド

このメソッドは、使用するフォームクラスを返します。
クラスにauthentication_form属性が設定されていれば、それを使用します。そうでなければ、form_class属性に設定されたデフォルトのフォームクラスを使用します。

get_form_kwargsメソッド

フォームに表示させるデフォルト情報の設定と、複数のフォームを区別させることができます。
ここでは親クラスの同名メソッドをオーバーライドして、requestオブジェクトを引数に加えています。
詳しくはこちらのget_form_kwargsメソッドで説明しています。

form_validメソッド

このメソッドは、フォームのバリデーションが成功した後に呼び出されます。
ユーザーをログインさせた後、get_success_urlメソッドで取得したURLにリダイレクトします。

リダイレクト後に後続の処理は実行されません。

get_context_data

このメソッドは、テンプレートに渡すコンテキストデータを構築します。
親クラスの同名メソッドを呼び出した後、リダイレクトURL、現在のサイト、サイト名などの追加情報をコンテキストに加えます。

RedirectURLMixinクラス

このミックスインは、リダイレクト先のURLを取得するための機能を提供しています。
LoginViewクラスで使用しているリダイレクト先のURLの取得は、このクラスをオーバーライドして使用しています。

RedirectURLMixin
class RedirectURLMixin:
    next_page = None
    redirect_field_name = REDIRECT_FIELD_NAME
    success_url_allowed_hosts = set()

    def get_success_url(self):
        return self.get_redirect_url() or self.get_default_redirect_url()

    def get_redirect_url(self):
        """Return the user-originating redirect URL if it's safe."""
        redirect_to = self.request.POST.get(
            self.redirect_field_name, self.request.GET.get(self.redirect_field_name)
        )
        url_is_safe = url_has_allowed_host_and_scheme(
            url=redirect_to,
            allowed_hosts=self.get_success_url_allowed_hosts(),
            require_https=self.request.is_secure(),
        )
        return redirect_to if url_is_safe else ""

    def get_success_url_allowed_hosts(self):
        return {self.request.get_host(), *self.success_url_allowed_hosts}

    def get_default_redirect_url(self):
        """Return the default redirect URL."""
        if self.next_page:
            return resolve_url(self.next_page)
        raise ImproperlyConfigured("No URL to redirect to. Provide a next_page.")
属性 説明
next_page デフォルトのリダイレクト先のURLを格納します。get_default_redirect_url メソッドで使用され、このURLが設定されていない場合にはエラーが発生します。
redirect_field_name リダイレクトのためのフィールド名を指定する属性です。リダイレクト先のURLを含むリクエストのパラメーター名を定義します
success_url_allowed_hosts 安全と見なされるホストのセットを格納します。リダイレクト先のURLがこのセットに含まれるホストによって提供されている場合にのみ、そのURLは安全と見なされます。

FormViewクラス

フォームの表示と処理を行います。
TemplateResponseMixinBaseFormViewを継承し、テンプレートレスポンスの生成とフォームの基本的な操作を提供します。

FormView
class FormView(TemplateResponseMixin, BaseFormView):

継承① TemplateResponseMixinクラス

テンプレートを使ってレスポンスを生成する機能を提供します。

TemplateResponseMixin
class TemplateResponseMixin:
    """A mixin that can be used to render a template."""

    template_name = None
    template_engine = None
    response_class = TemplateResponse
    content_type = None

    def render_to_response(self, context, **response_kwargs):
        """
        Return a response, using the `response_class` for this view, with a
        template rendered with the given context.

        Pass response_kwargs to the constructor of the response class.
        """
        response_kwargs.setdefault("content_type", self.content_type)
        return self.response_class(
            request=self.request,
            template=self.get_template_names(),
            context=context,
            using=self.template_engine,
            **response_kwargs,
        )

    def get_template_names(self):
        """
        Return a list of template names to be used for the request. Must return
        a list. May not be called if render_to_response() is overridden.
        """
        if self.template_name is None:
            raise ImproperlyConfigured(
                "TemplateResponseMixin requires either a definition of "
                "'template_name' or an implementation of 'get_template_names()'"
            )
        else:
            return [self.template_name]

render_to_responseメソッドは、指定されたテンプレートとコンテキストデータを使用して、TemplateResponseオブジェクトを生成します。
このオブジェクトを使用して、最終的なHTMLコンテンツをクライアント(ブラウザ)に送り返しています。

詳しくはこちらで説明しています。

継承② BaseFormViewクラス

BaseFormViewクラスはFormMixinクラスとProcessFormViewクラスを継承しています

FormMixinクラス

フォームの基本的な操作を行い、FormMixinProcessFormViewを継承します。

FormMixin
class FormMixin(ContextMixin):
    initial = {}
    form_class = None
    success_url = None
    prefix = None

    def get_initial(self):
        return self.initial.copy()

    def get_prefix(self):
        return self.prefix

    def get_form_class(self):
        return self.form_class

    def get_form(self, form_class=None):
        if form_class is None:
            form_class = self.get_form_class()
        return form_class(**self.get_form_kwargs())

    def get_form_kwargs(self):
        kwargs = {
            "initial": self.get_initial(),
            "prefix": self.get_prefix(),
        }

        if self.request.method in ("POST", "PUT"):
            kwargs.update(
                {
                    "data": self.request.POST,
                    "files": self.request.FILES,
                }
            )
        return kwargs

    def get_success_url(self):
        if not self.success_url:
            raise ImproperlyConfigured("No URL to redirect to. Provide a success_url.")
        return str(self.success_url)  # success_url may be lazy

    def form_valid(self, form):
        return HttpResponseRedirect(self.get_success_url())

    def form_invalid(self, form):
        return self.render_to_response(self.get_context_data(form=form))

    def get_context_data(self, **kwargs):
        if "form" not in kwargs:
            kwargs["form"] = self.get_form()
        return super().get_context_data(**kwargs)

このクラスからLoginViewクラスでオーバーライドしているメソッドは、get_form_classget_form_kwargs,form_valid,get_context_dataとなっています。

また、get_form_kwargsの説明で、get_initialget_prefixメソッドの説明もしているので、get_formのみ説明します。

get_form

form_class属性が設定されていない場合、get_form_classメソッドを使って取得します。

ProcessFormViewクラス

主にフォームの表示と処理を担当する基本的なビュークラスです。
HTTPのGETリクエストに対してフォームをレンダリングし、POSTリクエストでフォームのデータを処理するために使用されます。

ProcessFormView
class ProcessFormView(View):
    
    def get(self, request, *args, **kwargs):
        return self.render_to_response(self.get_context_data())

    def post(self, request, *args, **kwargs):
        form = self.get_form()
        if form.is_valid():
            return self.form_valid(form)
        else:
            return self.form_invalid(form)

    def put(self, *args, **kwargs):
        return self.post(*args, **kwargs)

Viewクラスを継承していますが、このクラスはリクエストメソッドに応じてそれに対応する名前のメソッドを実行しています。
つまりGETリクエストやPOSTリクエストを受け取れば、上記ProcessFormViewクラスの対応するメソッドが実行されるということです。

Viewの基底クラスについての説明はこちら

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?