【Django】LoginViewの使い方(クラスベースビューでのログインの実現)

LoginViewの使い方の解説ページアイキャッチ

このページにはプロモーションが含まれています

このページでは LoginView の使い方について説明していきます。

この LoginViewView というクラスのサブクラスの1つであり、ビューをクラスベースで作成する際に利用するクラスとなります。この View のサブクラス を継承し、さらにクラス変数を定義したりメソッドをオーバーライドすることで、あなたが開発したいアプリに応じたビューを作成することが可能となります。

この辺りのクラスベースビューやクラスベースビューの作り方については下記ページで解説していますので、詳しくはこちらをご参照ください。

クラスベースビューの解説ページアイキャッチ 【Django入門15】クラスベースビューの基本

このページで目指すことは、LoginView を利用し、上記ページで紹介しているログイン機能を持つウェブアプリに必要な動作や処理をクラスベースビューで実現していくことになります。

LoginView

まずは、LoginView がどういったクラスであるのかについて解説していきたいと思います。

名前の通り、LoginViewログインを実現するクラスとなります。

auth アプリから提供されるクラス

このサイトでは、今まで View のサブクラス として CreateViewUpdateView 等を紹介してきましたが、これらは django.views.generic から提供されるクラスでした。そのため、これらは django.views.generic から import して利用する必要がありました。

それに対し、LoginViewdjango.contrib.auth.views から import して利用することになります。

LoginViewのimport
from django.contrib.auth.views import LoginView

LoginViewauth というアプリで定義されるクラスとなりますので、上記のように import の仕方が他の View のサブクラス とは異なる点に注意してください。

また、auth アプリでは、下記のように認証やログイン等を実現するためのクラスや関数等が多数定義されており、LoginView は、下記のクラスや関数を利用してログインが実施されるように実装されたクラスとなります。 

  • ユーザーを管理するモデルクラス;User (カスタムユーザー)
  • ユーザー登録用のフォームクラス;UserCreationForm
  • ログイン用のフォームクラス:AuthenticationForm
  • 認証を行う関数:authenticate
  • ログインを行う関数:login

関数ベースビューの場合、上記のようなクラスや関数を利用して開発者自身がログインが実現できるような処理を実装する必要があるのですが、クラスベースビューの場合は、LoginView を継承するクラスを定義し、必要なカスタマイズを行うだけでログインが実現できることになります。

関数ベースビューでログインを実現する手順等については、下記ページで解説していますので、興味があれば読んでみていただければと思います。

ログインの実現方法の解説ページアイキャッチ 【Django入門10】ログイン機能の実現

スポンサーリンク

LoginView の処理の流れ

続いて LoginView の処理の流れについて説明しておきます。LoginView では、GET リクエストを受け取った際のログインページの表示と、POST リクエストを受け取った際の認証・ログイン処理によってログイン機能が実現されるようになっています。

LoginView は、まず GET リクエストを受け取った際にログイン用のフォームを含むログインページの表示を行います。このログイン用のフォームとは、先ほども紹介した AuthenticationForm となります。この AuthenticationForm ではユーザー名を入力する username フィールドとパスワードを入力する password フィールドが定義されており、ログインページ表示時にはこれらのフィールドを持つログイン用のフォームが表示されることになります。

GETリクエストを受け取った際のLoginViewの動作

このフォームから username フィールドと password フィールドを含むデータが POST リクエストとして送信されてきた際、LoginViewusername フィールドの値(ユーザー名)と password フィールドの値(パスワード)に基づいて認証を実施します。この認証では、入力されたユーザー名に対するパスワードが正しいかどうかの判断が行われます。また、この認証は AuthenticationForm のインスタンスから is_valid メソッドを実行させることで行われます(より細かくいうと、is_valid メソッドから authenticate という関数が実行されて認証が行われることになります)。

そして、認証が OK の場合のみ、username フィールドに入力されたユーザー名のユーザーに対するログイン処理を実施します。このログイン処理は login 関数の実行によって行われます。さらに、ログイン処理の後に特定の URL に対するリダイレクトレスポンスを返却します。

POSTリクエストを受け取った際のLoginViewの動作(認証OK時)

認証が NG の場合、例えば入力されたユーザー名のユーザーが登録されていない、入力されたパスワードが登録されているものと一致しないような場合は、再度ログイン用のフォームが表示されることになります。

POSTリクエストを受け取った際のLoginViewの動作(認証NG時)

このように、LoginViewログインを行うために必要となる “ログイン用のフォームの表示” や “認証処理”・”ログイン処理” が行われるように実装されているビューとなります。

LoginViewAUTH_USER_MODEL

この LoginView と密接に関連するのが settings.py で定義する AUTH_USER_MODEL になります。

まず、LoginView で実施されるログインとは、ユーザーをログイン状態に移行するための処理となります。そして、この『ユーザー』とは、具体的には settings.py で定義した AUTH_USER_MODEL に指定されたモデルクラスのインスタンスとなります。settings.pyAUTH_USER_MODEL が定義されていない場合は、AUTH_USER_MODEL にはデフォルトの User というモデルクラスが適用され、LoginView では、この User のインスタンスに対してログインが実施されることになります。

そのため、他のモデルクラス、例えば下記ページで解説している カスタムユーザー のインスタンスをユーザーとしてログインを実施したいような場合は、settings.py での AUTH_USER_MODEL の定義が必要となります。

【Django入門9】カスタムユーザーによるユーザー管理

また、先ほど LoginView では認証が実施されると説明しましたが、この認証は、フォームから送信されてきた username フィールドの値および password フィールドの値と同じフィールドの値を持つレコードがデータベースのテーブルに存在するかどうかの確認であり、存在する場合は認証 OK と判断されます。そして、この『テーブル』とは、AUTH_USER_MODEL に指定されたモデルクラスに対応するテーブルとなります(Django ではモデルクラスがデータベースのテーブルとして扱われます)。

したがって、認証で OK と判断されるためには、事前に AUTH_USER_MODEL のテーブルにレコードを新規登録しておく必要があります。このテーブルにレコードが存在しなければ必ず認証が NG となります。なので、ログインを実現するためには AUTH_USER_MODEL のテーブルにレコードを新規登録するビューも必要となります。

AUTH_USER_MODELにレコードを新規作成するビューが必要であることを示す図

ちなみに、このようなレコードの新規登録を行うビューは、下記ページで解説している CreateView を継承することで簡単に実現可能です。

CreateViewでのクラスベースビューの実現方法解説ページアイキャッチ 【Django】CreateViewの使い方(クラスベースビューでの新規登録ページの実現)

このように、LoginViewAUTH_USER_MODEL のテーブルや、AUTH_USER_MODEL のインスタンスを利用して動作するようになっています。この点についてはしっかり覚えておいてください。

LoginView でのクラスベースビューの作り方

では、次は LoginView でクラスベースビューを作成する手順について説明していきます。

LoginView でのクラスベースビューの作り方に関しても基本的には他の View のサブクラス と同じです。

つまり、views.pyLoginView を継承するクラスを定義し、そのクラスでクラス変数やメソッドを定義して LoginView 側で定義されているクラス変数の上書きメソッドのオーバーライドを行なうことで、ビューを作成していきます。これにより、LoginView の特徴を活かしながら自身のウェブアプリに応じたビューにカスタマイズしていくことが可能となります。

クラスベースビューの作り方の説明図

また、LoginView ではリクエストに応じて異なる処理が行われるようになっており、具体的には GET リクエストを受け取った際には  get メソッドが、POST リクエストを受け取った際には post メソッドが実行されるようになっています。そして、LoginView のクラス変数や他のメソッドは、これらの get メソッドおよび post メソッドから利用されるようになっています。したがって、LoginView のサブクラス側でクラス変数やメソッドを上書き・オーバーライドしてやれば、この get メソッドと post メソッドの動作を変化させることができます。

LoginViewのgetメソッドやpostメソッドからクラス変数や他のメソッドが利用される様子

スポンサーリンク

最低限のカスタマイズで利用可能

CreateViewFormView 等の他の View のサブクラス は基本的に開発者がカスタマイズすることを前提として用意されているビューとなります。それに対し、LoginViewFormView をカスタマイズすることで作られたビューで、LoginView はログインに特化する形で既に作り込まれたビューとなります。

そのため、LoginView に関しては最低限のカスタマイズのみで利用可能なビューとなっています。

例えば、CreateView のサブクラスではクラス変数 form_class (or model + fields) の定義を行なってビューで扱うモデルフォームクラスを必ず指定する必要がありました。これは、CreateView がどんなモデルフォームクラスでも対応できるような汎用的な作りになっているからになります。

それに対し、LoginView に関してはログインに特化しているため、あらかじめログイン用フォームとして form_classAuthenticationForm が指定されるようになっています。そのため、LoginView のサブクラスでは、わざわざカスタマイズを行って扱うフォームクラスを指定する必要はありません。

この例のように、LoginView はログインに特化する形で既に作り込まれているため、最低限のカスタマイズで利用可能となります。

他のビューのアクセス制限

ただし、ログイン機能を搭載するウェブアプリにおいては、LoginView のサブクラス以外のビューに対して別途カスタマイズが必要になることが多いので、その点には注意が必要です。

ログイン機能を搭載するウェブアプリの場合、特定のページに関しては非ログインユーザーからのアクセスを制限することが多いです。そもそも、ログインしないと利用できないウェブアプリも多いですよね。

LoginRequiredMixin の継承

そういった非ログインユーザーからのアクセスを制限するページを実現するためには、そのページの表示を行うビューに LoginRequiredMixin を継承させる必要があります。

例えば、下記のような CommentList によって表示されるページは、非ログインユーザーからもアクセス可能となります。

非ログインユーザーもアクセス可能なビュー
from django.views.generic import ListView
from .models import Comment

class CommentList(ListView):
    model = Comment

それに対し、下記のように LoginRequiredMixin を継承させるようにした場合、非ログインユーザーからのアクセスは不可となります。

非ログインユーザーはアクセス不可なビュー
from django.views.generic import ListView
from .models import Comment
from django.contrib.auth.mixins import LoginRequiredMixin

class CommentList(LoginRequiredMixin, ListView):
    model = Comment

このように、ログイン機能を搭載するウェブアプリを開発する場合は LoginView のサブクラスだけでなく他のビューに対するケアも必要になります。また、クラスベースビューの場合は上記のように LoginRequiredMixin を継承させるようにすれば良いのですが、関数ベースビューの場合は @login_required というデコレーターを利用することになります。これに関しての詳細は下記ページを参照してください。

ログインの実現方法の解説ページアイキャッチ 【Django入門10】ログイン機能の実現

LoginRequiredMixin によるリダイレクト

また、上記のようなアクセス制限はリダイレクトによって実現されることになります。

つまり、LoginRequiredMixin を継承したビューに対して非ログインユーザーがリクエストを送信してきた場合、ビューのメソッドが実行されるのではなく、リダイレクトレスポンスが返却されることになります。そして、このリダイレクトレスポンスを受け取ったウェブブラウザは、そのリダイレクト先の URL へのリクエスト(メソッドは GET)をウェブアプリに対して送信することになり、それによって元々アクセスしようとしていたページとは異なるページが表示されることになります。

LoginRequiredMixinによるアクセス制限の仕組みを説明する図

このリダイレクト先の URL は下記となります。

LOGIN_URL/?next=ログイン前にアクセスしようとしたページのURL

この LOGIN_URL の具体的な URL に関しては settings.pyLOGIN_URL を定義することで設定可能です。また、この LOGIN_URL にはログインページの URL を設定しておくことが一般的で、これにより、非ログインユーザーが LoginRequiredMixin にアクセスを拒否された際にはログインページにリダイレクトされることになり、ユーザーがわざわざログインページを開く手間を省くことができます。

LOGIN_URLにログインページのURLを設定したときの各種ビューの動作を説明する図

例えば、アプリのログインページの URL が /forum/login/ であり、さらに非ログインユーザーの /forum/comments/ の URL へのアクセスが LoginRequiredMixin によって制限されているとしましょう。この場合、settings.pyLOGIN_URL = '/forum/login/' を定義しておけば、非ログインユーザーが /forum/comments/ にアクセスすると下記 URL にリダイレクトされることになります。

/forum/login/?next=/forum/comments/

/forum/login/ はログインページの URL であるため、このリダイレクトによってログインページが表示されることになり、ユーザーはこのページでログインを実施することができます。

また、上記の URL における ?next=/forum/comments/ は、ユーザーが ログイン前にアクセスしようとした URL を示すクエリパラメーターとなります。このクエリパラメーターを利用することで、ログイン後の「ログイン前にアクセスしようとした URL へのリダイレクト」が実現できることになります。これに関しては、後述の next フィールドを利用したリダイレクト で説明します。

LOGIN_URL の定義

で、先ほど説明したように、LoginRequiredMixin によってアクセスが拒否された際には settings.py で定義された LOGIN_URL へのリダイレクトレスポンスが返却されることになります。LOGIN_URL を定義しなかった場合は、デフォルト設定の /accounts/login/ へのリダイレクトレスポンスが返却されることになります。

したがって、アクセス拒否時のリダイレクト先の URL を /accounts/login/ 以外に設定したい場合は、settings.py での LOGIN_URL の定義が必要となります。基本的には、この LOGIN_URL にはログインページの URL を指定するので良いです。

例えば、ウェブアプリのログインページの URL が /forum/login/ なのであれば、settings.py には下記のように LOGIN_URL を定義すればよいことになります。

LOGIN_URLの定義
LOGIN_URL = '/forum/login/'

スポンサーリンク

AUTH_USER_MODEL の定義

さらに、LoginView と AUTH_USER_MODEL で解説したように、LoginView でのログインや認証は AUTH_USER_MODEL に指定されたモデルクラスのテーブルやインスタンスを利用して実施されることになります。AUTH_USER_MODEL のデフォルトは User という Django フレームワークの auth というアプリで定義されたモデルクラスとなります。

ウェブアプリで別途ユーザーを管理するためのモデルクラスを定義し、LoginView にそれを利用させるようにするのであれば、settings.py での AUTH_USER_MODEL の定義が必要となります。例えば、アプリ名 forummodels.py に定義した CustomUser というモデルクラスを利用させるようにするためには、下記のように settings.pyAUTH_USER_MODEL を定義してやれば良いです。

AUTH_USER_MODELの定義
AUTH_USER_MODEL = 'forum.CustomUser'

ただし、AUTH_USER_MODEL に指定するモデルクラスはログインや認証に利用されるため、ユーザーを識別するフィールドやパスワードを管理するフィールドも必要となりますし、そのパスワードを暗号化するメソッド等も必要になります。なので、基本的には AUTH_USER_MODEL には AbstractUserAbstractBaseUser というモデルクラスのサブクラスとして定義した カスタムユーザー を指定する必要があります。この カスタムユーザー については下記ページで解説していますので、詳細は下記ページを参照してください。

【Django入門9】カスタムユーザーによるユーザー管理

ログイン後のリダイレクト先の設定

また、LoginView では、メソッドが POST のリクエストを受け取ってログインを行なった後にはリダイレクトレスポンスの返却が行われるようになっています。LoginView を利用してログインを実現する場合、このリダイレクトレスポンスにおけるリダイレクト先の URL の設定も必要となります。

LoginView においては、リダイレクト先の URL は下記3つの方法で設定可能です。下記の内の複数で URL が設定されている場合、上から順に優先してリダイレクト先の URL に設定されることになります。

  1. ログインフォームから送信するデータの next フィールドで指定する
  2. LoginView のサブクラスのクラス変数 next_page で指定する
  3. settings.py の LOGIN_REDIRECT_URL で指定する

設定方法が複数あってややこしいようにも思えますが、まずは 2. or 3. の方法でリダイレクト先の URL を設定してやるので良いと思います。具体的な設定方法に関しては、後述の next_page の節で解説を行います。

ただし、1. の方法でリダイレクト先の URL を設定することで、ログイン後の ログイン前にアクセスしようとした URL へのリダイレクトを実現することが可能となります。そのため、これを実現したいのであれば、1. の方法でのリダイレクト先の URL の設定が必要となります。これに関しては next フィールドを利用したリダイレクト で説明します。

テンプレートファイルを用意する

LoginView ではメソッドが GET のリクエストを受け取った時にログインページの表示を行うようになっています。この際にテンプレートファイルが利用されることになるため、テンプレートファイルを用意しておく必要があります。

このログインページでは、ログインフォームを表示することになるため、フォームが表示可能なテンプレートファイルを用意しておく必要があります。また、詳細は LoginView が生成するコンテキスト で解説しますが、LoginView が GET メソッドのリクエストを受け取った時に生成するコンテキストでは、form キーにフォームクラスのインスタンスがセットされるようになっています。

つまり、LoginView を利用してログインを実現する場合、少なくとも下記のような form タグを含むテンプレートファイルを用意しておく必要があります。

フォームの表示
<form action="ログインページのURL" method="post">
    {% csrf_token %}
    <table>{{ form.as_table }}</table>
    <p><input type="submit" value="送信"></p>
</form>

上記はフォームのフィールドをテーブル形式で出力することを前提としたものになっているため、出力形式に合わせて変更する必要があるので注意してください。このあたりの Django におけるフォームの表示に関しては下記ページで詳しく解説していますので、必要に応じて下記ページを参照していただければと思います。

Djangoのフォームの解説ページアイキャッチ 【Django入門5】フォームの基本

また、このテンプレートファイルの作り方を工夫することで、ログイン後の「ログイン前にアクセスしようとした URL へのリダイレクト」を実現することが可能です。これに関しては、後述の next フィールドを利用したリダイレクト で詳細を解説します。

スポンサーリンク

ログアウト用のビューを定義する

また、ログインを実現するのであればログアウトも必要となります。

ログアウトに関しては下記ページで解説している LogoutView を継承するサブクラスで実現可能です。LogoutView でログインを実現する例は、最後の LoginView の利用例 で示します。

LogoutViewの使い方の解説ページアイキャッチ 【Django】LogoutViewの使い方(クラスベースビューでのログアウトの実現)

LoginView のクラス変数

では、次は LoginView で定義されているクラス変数の紹介を行なっていきます。これらのクラス変数を LoginView を継承するクラスで定義し直して上書きすることでクラスの動作のカスタマイズを行うことが可能となります。

LoginView の場合、GET メソッドのリクエストを受け取った際に get メソッドが実行され、POST メソッドのリクエストを受け取った際に post メソッドが実行されることになるため、これらの get メソッドや post メソッドの動作を変化させることを目的にクラス変数の定義を行なっていくことになります。

LoginView のクラス変数の一覧

この LoginView で定義されるクラス変数には下記のようなものが存在します。

後半に示したクラス変数に関しては、他の View のサブクラス の解説ページで既に説明済みです。そのため、各クラス変数のテキストには、それらのページへのリンクを貼っています。他のページにジャンプするリンクテキストの横には括弧内でジャンプ先のページを示していますので、これらのクラス変数の詳細を知りたい方はリンク先の説明を参照していただければと思います。

ここでは、上記の前半部分に示した、特に LoginView ならではの特徴を持つクラス変数について説明していきます。

スポンサーリンク

form_class

form_classLoginView のサブクラスで扱うフォームクラスを指定するクラス変数になります。CreateViewUpdateView、さらには FormView 等も同じクラス変数を持っていますが、LoginView の場合はログイン用(認証用)のフォームクラスを指定する必要があります。ここが他の View のサブクラス とは異なります。

また、LoginView の場合、クラス変数 form_class を定義しなくても、デフォルトで form_classAuthenticationForm が指定されるようになっているため、form_class を定義しなくても自動的にLoginView が利用するフォームクラスが設定されるようになっています。

そして、この AuthenticationForm はユーザー名とパスワードの入力フィールドが定義されたクラスになります。このクラスのインスタンスに is_valid メソッドを実行させればパスワード認証が実行されます(is_valid メソッドは LoginViewPOST リクエストを受け取った際、ユーザーが入力したユーザー名やパスワードに対して実施されるようになっています)。このように、AuthenticationForm はログイン用フォームとして必要なフィールドや機能を備えており、基本的には form_class は定義せず、デフォルトの AuthenticationForm を利用するのでよいと思います。

AuthenticationFormによって表示されるフォームのフィールド

それでも AuthenticationForm 以外のフォームクラスを LoginView のサブクラスに利用させるようにしたいのであれば、LoginView を継承するクラスでクラス変数 form_class を定義してやればよいです。ただし、LoginView はあくまでもログインを実現するためのクラスですので、LoginView が利用するフォームクラスはログインに適したものである必要があります。そのため、基本的には AuthenticationForm のサブクラスのみをクラス変数 form_class に指定するようにした方が良いと思います。

authentication_form

authentication_form も基本的には form_class と同様の意味合いのクラス変数になります。すなわち、LoginView のサブクラスが利用するフォームクラスを指定します。デフォルト値は None になっており、クラス変数 authentication_form が定義されてフォームクラスが指定されている場合は、form_class ではなく authentication_form の方が優先して利用されることになります。

next_page

next_pageログイン後のリダイレクト先の設定 で説明したようにログイン後のリダイレクト先を指定するためのクラス変数になります。next_page には URL を表す文字列だけでなく、urls.py で設定する URL の名前も指定可能です。

例えば下記のように LoginView のサブクラスとして UserLogin を定義した場合、この UserLogin がメソッドが POST のリクエストを受け取ってログインが実行された後には、URL の名前が 'top-page' に設定されている URL へのリダイレクトレスポンスが返却されることになります。

next_pageの定義例
class UserLogin(LoginView):
    next_page = 'top-page'

LoginView では、前述の通り、ログイン後のリダイレクト先の URL は下記の3つの方法で設定可能です。複数の方法で設定された場合は、上側の設定が優先して採用されることになります。

  1. ログインフォームから送信するデータの next フィールドで指定する
  2. LoginView のサブクラスのクラス変数 next_page で指定する
  3. settings.py の LOGIN_REDIRECT_URL で指定する

なので、このクラス変数 next_page でのリダイレクト先の URL の設定は、1. でリダイレクト先の URL が設定されない場合、つまりログインフォームから送信されるデータに next フィールドが存在しない or next フィールドが空の場合に適用されることになります。

また、1. でリダイレクト先の URL が設定されない、かつ、LoginView のサブクラスでクラス変数 next_page が定義されていない場合は、LOGIN_REDIRECT_URL で指定される URL がリダイレクト先の URL として設定されることになります。もし settings.py で LOGIN_REDIRECT_URL が定義されていない場合は、LOGIN_REDIRECT_URL のデフォルト値である /accounts/profile/ がリダイレクト先として設定されることになります。

next フィールドを利用したリダイレクト でも説明をしますが、LoginView のサブクラスでログインを実現する場合、リダイレクト先の URL を /accounts/profile/ 以外に設定したいのであれば、2. or 3. の方法でのリダイレクト先の URL の設定が必須となります。

また、クラス変数 next_page でのリダイレクト先の URL の設定は、そのクラス変数を定義したビューでログインを行った場合に対してのみ有効となります。それに対し、settings.py での LOGIN_REDIRECT_URL はプロジェクト全体のログイン後のリダイレクト先の URL を設定するものとなります。これらの違いを理解した上で、2. or 3. の方法でリダイレクト先の URL の設定を行うようにしましょう。

スポンサーリンク

redirect_field_name

このクラス変数 redirect_field_name については、next フィールドを利用したリダイレクト の節で詳細を解説します。

redirect_authenticated_user

redirect_authenticated_user はログイン済みのユーザーが再度ログインページにアクセスした場合の動作を決定するクラス変数です。具体的には、ログイン済みのユーザーが LoginView のサブクラスが表示するページにアクセスしてきた際、redirect_authenticated_userFalse の場合はそのままログインページを表示されることになります。

ログインページへのアクセス時のredirect_autheticated_userがFalseの場合のレスポンス

それに対し、 redirect_authenticated_user を True に指定しておくと、ログイン済みのユーザーが LoginView のサブクラスが表示するページにアクセスした際に、特定の URL へのリダイレクトレスポンスが返却されるようになります。つまり、ログイン状態のユーザーはログインページを表示できなくなります。既にログイン済みのユーザーにログインページを表示する必要もないので、他のページにリダイレクトするというわけです。

ログインページへのアクセス時のredirect_autheticated_userがTrueの場合のレスポンス

また、このリダイレクト先は、ログイン後のリダイレクト先の URL と同じ URL となります。

template_name

template_name は、他の View のサブクラス 同様に LoginView のサブクラスが利用するテンプレートファイルを指定するクラス変数となります。デフォルトは 'registration/login.html' となります。また、このテンプレートファイルは、メソッドが GET のリクエストを受け取った時にログインフォームを表示するために利用されることになります。

また、下記ページで解説している通り、LogoutView が利用するデフォルトのテンプレートファイルのパスは 'registration/logged_out.html' で、このパスには Django フレームワークに用意されたアプリの admin によって用意されたテンプレートファイルが存在します。なので、LogoutView が利用するテンプレートファイルは用意しなくても、'registration/logged_out.html' を利用してログアウト後のページ表示が可能となります。

LogoutViewの使い方の解説ページアイキャッチ 【Django】LogoutViewの使い方(クラスベースビューでのログアウトの実現)

ですが、LoginView のデフォルトのテンプレートファイルのパスである 'registration/login.html' にはファイルが存在しませんので、別途このパスにテンプレートファイルを設置する or クラス変数 template_name を定義し、この template_name に指定したパスに自身で作成したテンプレートファイルを設置する必要があります。この点が LogoutView と異なりますので注意してください。

スポンサーリンク

LoginView のメソッド

次に LoginView の持つメソッドを紹介していきます。LoginView を継承したクラスを定義し、そのクラスで LoginView の持つメソッドをオーバーライドしてやることで LoginView とは異なる動作のクラスを実現することができるようになります。

前述でも説明しましたが、LoginView はログイン用のビューとして既に作り込まれているため、メソッドのオーバーライドを行う機会は少ないのではないかと思います。

ということで、ここではオーバーライドの例などは示さず、LoginView のメソッドの一覧のみを示しておきたいと思います。

LoginView のメソッド一覧

LoginView の持つメソッドの一覧は下記のようになります。あくまでもクラスのカスタマイズ目的でオーバーライドを行う可能性のあるものを挙げており、as_view などのカスタマイズは行わないであろうメソッドは省略しています。

  • dispatch:リクエストのメソッドに応じて get or post を実行する、もしくはリダイレクトレスポンスを返却する
  • get:リクエストのメソッドが GET の場合の処理を実行する(ログインフォームを表示する)
  • post:リクエストのメソッドが POST の場合の処理を実行する(ログインとリダイレクトレスポンスの返却を行う)
  • get_context_data:テンプレートに渡すコンテキストを生成する
  • get_form:フォームクラスのインスタンスを取得する
  • get_form_class:フォームクラスを取得する
  • get_form_kwargs:フォームクラスのコンストラクタに指定するパラメータを取得する
  • get_initialinitial への指定値を取得する
  • get_prefixprefix への指定値を取得する
  • form_valid:ログインを実行し、その後に特定の URL へのリダイレクトレスポンスを返却する(妥当性の検証が OK の時に実行される)
  • form_invalid:再度フォームを表示する(妥当性の検証が NG の時に実行される)
  • get_success_url:リダイレクト先の URL を取得する
  • get_redirect_url:非ログイン状態でアクセスしようとしていた URL を取得する
  • get_default_redirect_urlnext_page or LOGIN_REDIRECT_URL で指定される URL を取得する
  • get_template_names:テンプレートファイルの名前を取得する
  • render_to_response:レスポンスを返却する

ポイントとなるメソッドの1つが dispatch となります。リクエストを受け取った際に最初に実行される LoginView のメソッドは dispatch となり、クラス変数 redirect_authenticated_userTrue の場合、アクセスしてきたユーザーが既にログイン中であればリダイレクトが行われることになります。

それ以外の場合は、リクエストのメソッドの種類(GET or POST)に応じて get or postdispatch から実行されるようになっています。そして、これらの getpost の中から上記の各種メソッドが実行されるようになっています。

また、ログイン後のリダイレクト先の URL は get_success_url メソッドが実行されることで決定されることになります。そのため、この get_success_url メソッドをオーバーライドすることで、下記の3つ以外の方法でリダイレクト先の URL を設定することも可能となります。

  1. ログインフォームから送信するデータの next フィールドで指定する
  2. LoginView のサブクラスのクラス変数 next_page で指定する
  3. settings.py の LOGIN_REDIRECT_URL で指定する

LoginView が生成するコンテキスト

続いて LoginView が生成するコンテキストについて説明しておきます。

LoginView は get_context_data メソッドの中でコンテキストを生成し、その生成するコンテキストには下記の要素が含まれます。

  • 'form'AuthenticationForm のインスタンス
  • 'next':クエリパラメーター next で指定された URL
  • 'site':ログインしようとしているサイト(アプリ)の情報
  • 'site_name':ログインしようとしているサイト(アプリ)の名前

これらの中でテンプレートファイルから参照する機会が多いのが 'form' キーと 'next' キーの値になると思います。

'form' キーの値には AuthenticationForm のインスタンスがセットされており、これをテンプレートファイルから {{ form.as_table }} 等で出力すれば、通常のフォームクラスと同様にフォームの表示を行うことができます。form_class や authentication_form の節で説明したように、クラス変数 form_class or authentication_form を定義することで ログインフォームを AuthenticationForm 以外のものに設定することが可能です。そして、この場合は 'form' キーには、form_class or authentication_form で指定されたフォームクラスのインスタンスがセットされることになります。

また、'next' キーの値にはクエリパラメーター next で指定された URL がセットされており、これを上手く利用することで、 ログイン後の「ログイン前にアクセスしようとした URL へのリダイレクト」を実現することができます。この「ログイン前にアクセスしようとした URL へのリダイレクト」に関しては、次の next フィールドを利用したリダイレクト で詳細を解説していきます。また、この 'next' のキー名は、クラス変数 redirect_field_name によって変更可能です。これに関しても next フィールドを利用したリダイレクト で解説します。

もし、上記以外のデータをコンテキストにセットしてテンプレートファイルから参照できるようにしたいのであれば、ListView の解説ページの extra_context で説明しているクラス変数 extra_context を定義したり、get_context_data のオーバーライドを行なったりしてコンテキストの要素を追加することも可能です。

スポンサーリンク

next フィールドを利用したリダイレクト

続いて next フィールドを利用したリダイレクトについて解説していきます。

next フィールドを利用したリダイレクトで実現できること

ログイン機能を備えたウェブアプリやウェブサイトでは、非ログイン状態でウェブアプリのページを開いた時にはログインページにリダイレクトされ、さらにそのページでログインを行うと、ログイン前にアクセスしようとした URL にリダイレクトされるようなものが多いです。

ログイン前にアクセスしようとしたURLへのリダイレクトの説明図

Django で開発するウェブアプリにおいても、next フィールドを利用したリダイレクトにより、このような、ログアウト後の「ログイン前にアクセスしようとした URL へのリダイレクト」を実現することができます。

ログイン前にアクセスしようとした URL へのリダイレクトの実現方法

この実現方法の具体的な手順を、ここで説明していきます。

フォームから next フィールドを含んだデータを送信させることが必要

まず、ログイン後のリダイレクト でも説明したように、LoginView でのログイン後のリダイレクト先の URL は下記の3つの方法で設定することができます。

  1. ログインフォームから送信するデータの next フィールドで指定する
  2. LoginView のサブクラスのクラス変数 next_page で指定する
  3. settings.py の LOGIN_REDIRECT_URL で指定する

ログイン前にアクセスしようとした URL へのリダイレクトは、上記の 1. によって実現することになります。

上記のようにリダイレクト先の URL が設定できるようになっているため、ログインフォームから nextフィールドを含むデータを送信できるようにし、さらに、その nextフィールドの値に ログイン前にアクセスしようとした URL がセットされるようになれば、後は LoginView がログイン後に ログイン前にアクセスしようとした URL にリダイレクトしてくれることになります。

フォームから送信されるデータにnextフィールドが存在すれば、ログイン後にそのフィールドの値をリダイレクト先とするリダイレクトレスポンスが返却されることを説明する図

問題は、このログインフォームから送信されるデータの nextフィールドの値に、どうやって ログイン前にアクセスしようとした URL をセットするのか?という点になります。

この点について考えていきましょう!

コンテキストには ログイン前にアクセスしようとした URL が含まれる

まず、ここで LoginRequiredMixin によるリダイレクト の節での解説内容を思い出してみましょう。クラスベースビューのビューに LoginRequiredMixin を継承させれば、そのビューによって表示されるページに非ログインユーザーがアクセスするとリダイレクトレスポンスの返却が行われるようになるのでしたね!

そして、そのリダイレクト先の URL は下記となります。

LOGIN_URL/?next=ログイン前にアクセスしようとしたページのURL

リダイレクトレスポンスを受け取ったクライアントは、このリダイレクト先の URL に対するメソッド GET のリクエストを送信することになります。また、上記における LOGIN_URLsettings.py で定義することが可能で、通常ログインページの URL が指定されることになります。したがって、ログインを LoginView のサブクラスで実現している場合、LoginRequiredMixin によるリダイレクトによって、上記の URL & メソッド GET のリクエストを LoginView のサブクラスが受け取ることになります。

LoginRequiredMixinによるリダイレクトレスポンスのリダイレクト先のURLのクエリパラメーターnextで、ログイン前にアクセスしようとしたURLが指定されることを示す図

これにより、LoginView の get メソッドが実行されることになります。そして、この get メソッドで、ログインフォームを表示するためのコンテキストが生成され、そのコンテキストとテンプレートファイルからログインフォーム表示用の HTML が生成されることになります。ポイントは、この LoginView が受け取るリクエストの URL では、上記のようにクエリパラメーター nextログイン前にアクセスしようとした URL が指定される点になります。

次に、ここで思い出していただきたいのが LoginView が生成するコンテキスト で説明した LoginView が生成するコンテキストの中身です。LoginView が生成するコンテキストを下記に再掲しておきます。ご覧の通り、このコンテキストには next キーが存在し、このキーの値は「クエリパラメーター next で指定された URL」となります。そして、前述の通り、LoginRequiredMixin によってリダイレクトされた際のクエリパラメーター next では ログイン前にアクセスしようとした URL が指定されることになります。

  • 'form'AuthenticationForm のインスタンス
  • 'next':クエリパラメーター next で指定された URL
  • 'site':ログインしようとしているサイト(アプリ)の情報
  • 'site_name':ログインしようとしているサイト(アプリ)の名前

したがって、コンテキストの next キーの値は ログイン前にアクセスしようとした URL ということになります。

つまり、非ログインユーザーからのアクセスを制限したいビューに対して LoginRequiredMixin を継承させておけば、そのアクセスの拒否によってログインページにリダイレクトされて LoginViewget メソッドが実行された際には、next キーの値に ログイン前にアクセスしようとした URL がセットされたコンテキストが生成されるということになります。

next フィールドが送信されるフォームを作成する

また、コンテキストにセットされている値はテンプレートファイルに埋め込むことが可能です。また、テンプレートファイルは開発者が自由に作成することができます。当然、開発者の好きなようにフォーム要素を定義することも可能です。

なので、コンテキストに ログイン前にアクセスしようとした URL がセットされているのであれば、ログインフォームに対して、値が ログイン前にアクセスしようとした URL である next フィールドを追加することも可能です。これによって、元々実現したかった「値が ログイン前にアクセスしようとした URL である nextフィールドを含むデータのフォームからの送信」が実現できることになります。ただ、この next フィールドはユーザーから入力受付を行うものではないので非表示にしてやるのが良いと思います。

ログインフォームにnextフィールドを追加した様子

具体的には、このようなフォームは、テンプレートファイルに下記のような form タグを記述することで実現できます。テンプレートファイルを用意する で示したテンプレートファイルに対し、name が nextinput タグを追加しています。これが next フィールドとなり、値は {{ next }}、すなわちコンテキストの next キーの値となります。

nextフィールドの送信を行うフォーム
<form action="ログインページのURL" method="post">
    {% csrf_token %}
    <table>{{ form.as_table }}</table>
    <input type="hidden" name="next" value="{{ next }}">
    <p><input type="submit" value="送信"></p>
</form>

<form> ~ </form> 内のボタン要素がクリックされた時には、<form> ~ </form> 内の全フィールドの値が送信先へ送信されることになりますので、上記のように next フィールドを <form> ~ </form> 内に追加すれば、{{ form.as_table }} によって出力されるフィールド(usernamepassword)に追加して、next フィールドの値もボタン要素クリック時に送信されるようになります。

ログインフォームからのデータ送信時にnextフィールドの値も送信される様子

また、この next フィールドには type="hidden" が指定されているためページには表示されません。なので、ユーザーから値は基本的には変更不可で、送信される next フィールドの値はコンテキストの next キーの値、すなわち ログイン前にアクセスしようとした URL となります。

さらに、form タグには上記のように action 属性と method 属性が指定されていますので、ボタンクリック時には URL が ログインページのURL でメソッドが POST のリクエストが送信されることになります。各種フィールドは、そのリクエストのボディとして送信されます。

で、このリクエストをログインを LoginView のサブクラスで実現しているウェブアプリが受け取れば、LoginView のサブクラスの post メソッドが実行されることになり、ログイン実施後にリダイレクトレスポンスの返却が行われることになります。そして、そのリダイレクト先の URL には、フォームから送信されてきたデータに next フィールドが存在するため、その next フィールドの値、すなわち ログイン前にアクセスしようとした URL が設定されることになります。つまり、ログイン後の「ログイン前にアクセスしようとした URL 」へのリダイレクトが実現できることになります。

フォームから送信されるデータにnextフィールドが存在すれば、ログイン後にそのフィールドの値をリダイレクト先とするリダイレクトレスポンスが返却されることを説明する図

説明が長くなりましたが、結論としては、LoginView のサブクラスでログインを実現する場合、ログイン後の ログイン前にアクセスしようとした URL へのリダイレクトを実現するために必要な作業は下記の2つのみとなります。

  1. アクセス制限したいビューに LoginRequiredMixin を継承させる
  2. ログインフォームを表示するテンプレートファイルに下記の属性を指定した input タグを記述する
    • name="next" 
    • value="{{ next }}" 

上記の2つの両方を行えば、後は LoginView が、ログイン後に ログイン前にアクセスしようとした URL へリダイレクトされるよう動作してくれます。

逆に、ログイン後の ログイン前にアクセスしようとした URL へのリダイレクトを行いたくない場合は、テンプレートファイルへの上記のような name が nextinput タグの記述をやめてしまえば良いです。これによってフォームから送信されるデータには next フィールドが存在しなくなるため、next フィールドによるリダイレクト先の URL の設定が行われなくなります。

スポンサーリンク

next フィールドを利用したリダイレクトの注意点

ここまで説明してきた通り、LoginView ではフォームから送信されてくるデータの next フィールドをリダイレクト先の URL に設定することが可能です。

ただし、この next フィールドを利用したリダイレクトにはいくつか注意点が存在しますので、その点について説明しておきます。

next フィールドは空の場合がある

まず、フォームから next フィールドを含むデータを送信できるようにしたとしても、next フィールドが空になる場合があるという点に注意してください。

nextフィールドが空のデータが送信される様子

URL にクエリパラメーター next が自動的に追加されるのは LoginRequiredMixin を継承するビューに非ログインユーザーがアクセスしてリダイレクトされた時となります。したがって、これ以外の場合は URL にクエリパラメーター next が自動的に追加されないことになります。例えば、ログインページを表示するビューは、非ログインユーザーからもアクセスできるようにするため LoginRequiredMixin を継承してはいけません。そのため、非ログインユーザーでも直接ログインページにアクセスすることが可能で、この場合はクエリパラメーター next が追加されないことになります。

クエリパラメーター next が存在しない場合、前述のようにログイン後に ログイン前にアクセスしようとした URL へリダイレクトされるようにアプリを作成したとしても、フォームから送信される next フィールドの値は空となります。この場合、下記の 1. によるリダイレクト先の URL の設定が行われないことになります。

  1. ログインフォームから送信されるデータの nextフィールドでリダイレクト先の URL を設定する
  2. LoginView のサブクラスでクラス変数 next_page を定義する
  3. settings.pyLOGIN_REDIRECT_URLを定義する

で、この場合は、上記の 2. or 3. によってリダイレクト先の URL が設定されることになります。

つまり、1. でリダイレクト先の URL を設定できるようにアプリを開発したとしても、1. でのリダイレクト先の設定が行われないケースが存在するため、「LoginView のサブクラスでのクラス変数 next_page の定義」or「settings.py での LOGIN_REDIRECT_URL の定義」が必要ということになります。

ということで、LoginView のサブクラスを定義する場合、「LoginView のサブクラスでのクラス変数 next_page の定義」と「settings.py での LOGIN_REDIRECT_URL の定義」の少なくともどちらか一方は必ず実施するようにしてください(リダイレクト先が LOGIN_REDIRECT_URL のデフォルト値でも問題ない場合は定義不要です)。

クラス変数 redirect_field_name の定義でキー名・フィールド名が変化する

ここまで説明してきたように、 ログイン前にアクセスしようとした URL へのリダイレクトは、”next” という名称のクエリパラメーター・キー・フィールドを利用して実現することになります。具体的には、下記の箇所で “next” という名称のクエリパラメーター・キー・フィールドが利用されることになります。

  1. クエリパラメーター next で指定された URL が LoginView の生成するコンテキストの next キーに設定される
  2. フォームから送信されてきた next フィールドの値がリダイレクト先の URL に設定される

また、LoginView のサブクラスではクラス変数 redirect_field_name が定義可能で、このクラス変数 redirect_field_name の定義によって、上記における “next” という名称が変更可能となっています。

ただし、このクラス変数 redirect_field_name を定義すると、その定義に合わせて LoginView のサブクラス以外の変更も必要になるという点に注意してください。

まず、テンプレートファイルの変更が必要となります。例えば先ほど示した下記のフォーム要素は、{{ next }} によってコンテキストの next キーの値を参照するようになっています。さらに、name="next" の指定により、その値を next フィールドの値としてフォームから送信するようになっています。

nextフィールドの送信を行うフォーム
<form action="{% url 'login' %}" method="post">
    {% csrf_token %}
    <table>{{ form.as_table }}</table>
    <input type="hidden" name="next" value="{{ next }}">
    <p><input type="submit" value="送信"></p>
</form>

ここで、LoginView のサブクラスで下記のようにクラス変数 redirect_field_name を定義したとしましょう。

フィールド名等の変数名の変更
class UserLogin(LoginView):
    redirect_field_name = 'redirect'

クラス変数 redirect_field_name の定義によって、コンテキストのキー名、さらには、リダイレクト先の URL として扱うフィールド名が変化することになるため、それに合わせてテンプレートファイルの変更も必要となります。今回の場合であれば、下記のようにテンプレートファイルのフォーム要素を変更する必要があります。

redirectフィールドの送信を行うフォーム
<form action="{% url 'login' %}" method="post">
    {% csrf_token %}
    <table>{{ form.as_table }}</table>
    <input type="hidden" name="redirect" value="{{ redirect }}">
    <p><input type="submit" value="送信"></p>
</form>

また、LoginRequiredMixin を継承するビューも変更が必要となります。このビューは、非ログインユーザーからアクセスされた時にリダイレクトを行い、そのリダイレクト先の URL にクエリパラメーター next を追加するようになっています。デフォルトでは、LoginView のサブクラスは、そのクエリパラメーター next を参照して値をコンテキストにセットするようになっているのですが、クラス変数 redirect_field_name を定義すると、その参照先のクエリパラメーターの変数名が変化することになります。

LoginViewのサブクラスでredirect_field_nameを定義することで、参照されるクエリパラメーターの変数名が変化する様子

なので、LoginRequiredMixin を継承するビューでは、LoginView のサブクラスのクラス変数 redirect_field_name への指定値に合わせて、リダイレクト時に追加するクエリパラメーターの変数名を変更する必要があります。

具体的には、LoginRequiredMixin では、リダイレクト時に追加するクエリパラメーターはクラス変数 redirect_field_name によって変更できるようになっているため、LoginRequiredMixin を継承するクラスでクラス変数 redirect_field_name を定義し、その値として、LoginView のサブクラスのクラス変数 redirect_field_name と同じ値を指定してやれば良いことになります。

例えば、LoginView のサブクラスでクラス変数 redirect_field_name = 'redirect' を定義するのであれば、下記のように LoginRequiredMixin を継承するクラスでもクラス変数 redirect_field_name = 'redirect' を定義してやれば良いことになります。

クエリパラメーターの変更
class CommentList(LoginRequiredMixin, ListView):
    redirect_field_name = 'redirect'

基本的には、クラス変数 redirect_field_name をわざわざ定義する必要はないとは思いますが、何らかの理由で定義する必要がある場合、その定義に合わせて他のクラスやファイルの変更も必要になるので注意してください。

LoginView の利用例

最後に、ここまでの説明のまとめとして、LoginView を継承するクラスでのビューの作成例を示していきたいと思います。ここでは、LoginView だけでなく、LogoutView を利用したログアウトを実現する例や、非ログイン状態でアクセス不可なページとして ListView を利用する例も示していきます。

プロジェクトとアプリの作成

最初にいつも通りプロジェクトを作成していきしょう!。

まず、適当な作業フォルダに移動した後、下記コマンドでプロジェクトを作成します。今回はプロジェクトを loginviewproject としています。

% django-admin startproject loginviewproject

続いてアプリを作成していきます。先ほどのコマンドの実行で loginviewproject というフォルダが作成されたはずなので、この loginviewproject に下記の cd コマンドで移動します。

% cd loginviewproject

続いて、下記コマンドを実行してアプリを作成します。今回はアプリを accounts としています。

% python manage.py startapp accounts

また、先ほど cd コマンドで移動した先のフォルダの中にも loginviewproject というフォルダがあるはずなので、そのフォルダの下にある settings.py を開き、下記のように INSTALLED_APPS のリストを変更します。これにより、アプリ accounts がプロジェクトに登録されることになります。

settings.pyの変更
INSTALLED_APPS = [
    'accounts', # 追加
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

スポンサーリンク

フォームの定義とモデルの定義は省略

今回はフォームの定義とモデルの定義は行わず、auth アプリで定義される AuthenticationFormLoginView のサブクラスが扱うフォームクラスとし、さらにユーザーを管理するモデルクラスとして User を利用します。これらは認証やログイン等の機能を備えたクラスとなります。

もし LoginView のサブクラスが扱うフォームクラスを他のものにしたいのであれば、form_class や authentication_form で説明したように、これらのクラス変数を定義することで変更することも可能です。

また、ユーザーを管理するモデルクラスを User から変更したい場合は カスタムユーザー を別途定義し、それを利用するようにする必要があります。というか、そもそもユーザーを管理するモデルクラスとしては User をそのまま利用することは推奨されていません(お試しで開発するウェブアプリで利用することは問題ないです)。この辺りに関しては下記ページで解説していますので、必要に応じてご参照していただければと思います。

【Django】カスタムユーザー(独自のユーザー)の作り方【AbstractUser編】

今回はお試しのウェブアプリを開発していくだけなので、User をそのまま利用していきたいと思います。

マイグレーションの実施

今回は新たにモデルの定義は行いませんが、データベース内にテーブルは必要なので、そのテーブルの作成を行うためにマイグレーションを実施します。具体的には、今回のウェブアプリの場合、User のインスタンスをレコードとして保存するためのテーブルや、セッション情報のレコードを保存するためのテーブルが必要となります。

ただし、今回は models.py でのモデルクラスの定義は行っていないため、必要なコマンドは下記の1つのみとなります。

% python manage.py migrate

クラスベースビューの作成

続いて、本題のクラスベースビューの作成を行っていきます。今回は、ここまでの説明の通り LoginView のサブクラスを定義し、そのクラスをクラス変数の定義によってカスタマイズしていきます。また、ログアウトを実現するためのビューとして LogoutView のサブクラスの定義も行います。さらに、非ログインユーザーがアクセス不可なページを実現するビューとして ListView のサブクラス、ユーザーの新規登録を実現するビューとして CreateView のサブクラスを定義していきます。

今回は、下記のように views.py を変更し、LoginView のサブクラスとして UserLogin を定義したいと思います。また、動作確認を目的に、LogoutView のサブクラスとして UserLogoutListView のサブクラスとして UserListCreateView のサブクラスとして UserRegistration も定義しています。 

views.py
from django.views.generic import CreateView, ListView
from django.contrib.auth.views import LoginView, LogoutView
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth import login
from django.urls import reverse_lazy

class UserLogin(LoginView):
    template_name = 'accounts/user_login.html'
    redirect_field_name = 'redirect'
    redirect_authenticated_user = True

class UserLogout(LogoutView):
    pass

class UserRegistration(CreateView):
    form_class = UserCreationForm
    template_name = 'accounts/user_form.html'
    success_url = reverse_lazy('list')

    def form_valid(self, form):
        response = super().form_valid(form)
        user = self.object
        login(self.request, user)
        return response

class UserList(LoginRequiredMixin, ListView):
    model = User
    template_name = 'accounts/user_list.html'
    redirect_field_name = 'redirect'

UserLogin

UserLogin は、ここまで説明してきた LoginView のサブクラスであり、ログインを実現するビューとなります。

UserLogin はメソッドが GET のリクエストを受け取った際にはログインページを表示するための HTML を返却し、メソッドが POST のリクエストを受け取った際にはフォームから送信されてきたデータに基づいて認証を行い、認証結果が OK の場合にはログインの実施および特定の URL へのリダイレクトレスポンスの返却を行います。

クラス変数 redirect_field_name を定義しているため、UserLogin がリダイレクト先として扱う URL の取得先がクエリパラメーターの next から redirect に変化している点に注意してください。

また、UserLogin ではクラス変数 redirect_authenticated_user を定義して True を指定しているため、ログイン中のユーザーからのアクセスによって UserLogin が動作する際には即座に特定の URL へのリダイレクトレスポンスが返却されることになります。

さらに、UserLogin ではクラス変数 next_page を定義していないため、settings.py での LOGIN_REDIRECT_URL の定義が必要となります。この変更については後ほど説明します。

UserLogout

UserLogout は LogoutView を継承するクラスとなり、ユーザーログアウトを実現するビューとなります。

この LogoutView に関しては下記ページで詳細を解説していますので、詳しくは下記ページを参照してください。

LogoutViewの使い方の解説ページアイキャッチ 【Django】LogoutViewの使い方(クラスベースビューでのログアウトの実現)

LogoutView においてもログアウト実施後にリダイレクトが行われることになります。UserLogout においてもクラス変数 next_page を定義していないため、settings.py での LOGOUT_REDIRECT_URL の定義が必要となります。この変更についても後ほど説明します。

UserRegistration

UserRegistrationCreateView のサブクラスであり、ユーザーの新規登録を実現するビューとなります。より具体的には User のテーブルにインスタンス(レコード)を新規登録するビューとなります。

この CreateView に関しては下記ページで詳細を解説していますので、各クラス変数やメソッドの意味合いに関しては下記ページをご参照いただければと思います。

CreateViewでのクラスベースビューの実現方法解説ページアイキャッチ 【Django】CreateViewの使い方(クラスベースビューでの新規登録ページの実現)

ログインを実現する上での、ユーザーの新規登録を行うビューを定義するときの注意点は、そのインスタンスの新規登録先のテーブルが AUTH_USER_MODEL となるように定義する必要がある点になります。今回は settings.py での AUTH_USER_MODEL の定義は行わないため、この AUTH_USER_MODELUser となります。したがって、UserRegistration は、インスタンス(レコード)を User のテーブルに新規登録するようなビューである必要があります。

UserRegistration では、クラス変数 form_class を定義し、その form_classUserCreationForm を指定しているため、レコードの新規登録を行うときに利用するフォームクラスは UserRegistration となります。そして、この UserRegistration は Django フレームワークの auth アプリで定義されるモデルフォームクラスであり、そのベースとなるモデルクラスは User となります(model 属性に User が指定されている)。そのため、このモデルフォームクラスを利用したレコードの新規登録は User のテーブルに対して行われることになり、上記を満たしていることになります。

この説明の中で登場した UserRegistration については下記ページで、

【Django入門9】カスタムユーザーによるユーザー管理

モデルフォームクラスについては下記ページで解説していますので、詳細を知りたい方は別途これらのページを参照していただければと思います。

Djangoのモデルフォーム解説ページアイキャッチ 【Django入門8】モデルフォームの基本

別途自身で カスタムユーザー を定義するのであれば、settings.py で AUTH_USER_MODEL を定義し、その カスタムユーザー を AUTH_USER_MODEL に指定する必要があります。そして、この場合は UserRegistration を継承するクラスを定義し、model 属性に カスタムユーザー を指定する必要があります。このあたりに関しても上記で示した カスタムユーザー の解説ページで説明していますので、必要に応じて カスタムユーザー の解説ページを参照してください。

また、UserRegistration では form_valid を定義してオーバーライドを行なっており、CreateViewform_valid が行う処理に加え、login 関数によるログイン処理も実行するようになっています。そのため、ユーザーの新規登録に成功した際には、そのユーザーでのログインが自動的に実施されることになります。

UserList

UserList は ListView のサブクラスであり、ユーザーの一覧の表示を実現するビューとなります。より具体的には、UserListUser のインスタンスの一覧を表示します。

この ListView に関しては下記ページで詳細を解説していますので、各クラス変数やメソッドの意味合いに関しては下記ページをご参照いただければと思います。

DjangoのListViewの解説ページアイキャッチ 【Django】ListViewの使い方(クラスベースビューでの一覧リストページの実現)

この UserListLoginRequiredMixin を継承している点がポイントで、この LoginRequiredMixin の継承により UserList にマッピングされた URL へのアクセスは非ログインユーザーからは拒否され、LOGIN_URL をリダイレクト先とするリダイレクトレスポンスが返却されることになります。

この LOGIN_URL のデフォルト値は /accounts/login/ で、settings.py での LOGIN_URL の定義によって他の URL に変更可能です。今回は LOGIN_URL の定義は行わず、デフォルト値のまま LOGIN_URL を利用していきます。つまり、このウェブアプリのログインページの URL は /accounts/login/ ということになります。そのため、後ほどの urls.py の作成・変更時に、この /accounts/login/ にはログインを実現するビューである UserLogin をマッピングさせます。

また、next フィールドを利用したリダイレクト で解説したように、LoginView のサブクラスのクラス変数 redirect_field_nameLoginRequiredMixin を継承するクラスのクラス変数 redirect_field_name は同じ値とする必要があります。今回  LoginView のサブクラスである UserLogin では redirect_field_name = 'redirect' を定義しているため、UserList でも redirect_field_name = 'redirect' を定義するようにしています。

これにより、非ログインユーザーから UserList にマッピングされた URL にアクセスされた際のリダイレクト先の URL は下記となり、クエリパラメーター redirect によって ログイン前にアクセスしようとした URL が指定されるようになります。

/accounts/login/?redirect=ログイン前にアクセスしようとした URL

next フィールドを利用したリダイレクト で解説したように、このクエリパラメーター redirect を利用することで、ログイン後の「ログイン前にアクセスしようとした URL」へのリダイレクトが実現可能です。

スポンサーリンク

ビューと URL とのマッピング

次は views.py で定義したビューと URL とのマッピングを行なっていきます。

まず、loginviewproject フォルダの下にある urls.py を下記のように変更してください。

loginviewproject/urls.py
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('accounts/', include('accounts.urls')),
]

続いて、accounts フォルダの下に urls.py を新規作成し、中身を下記のように変更してください。

accounts/urls.py
from django.urls import path
from . import views

urlpatterns = [
    path('login/', views.UserLogin.as_view(), name='login'),
    path('logout/', views.UserLogout.as_view(), name='logout'),
    path('register/', views.UserRegistration.as_view(), name='register'),
    path('list/', views.UserList.as_view(), name='list'),
]

テンプレートの作成

次はテンプレートファイルを作成していきます。

最初にテンプレートファイルを設置するのに必要となるフォルダを準備しておきましょう。まずアプリフォルダ(accounts フォルダ)の中に templates フォルダを作成してください。次に、その templates フォルダの中に accounts フォルダを作成します。これでフォルダの準備は完了です。

今回 views.py で定義した各種ビューではクラス変数 template_name を定義して利用するテンプレートファイルのパスを指定するようにしているため、その指定したパスにテンプレートファイルを作成していきます。

user_login.html

まずは UserLogin が利用するテンプレートファイルとして user_login.html を用意していきます。先ほど作成した accounts フォルダの中に user_login.html を新規作成し、中身を下記のように変更してください。

user_login.html
<!doctype html>
<html lang="ja">
<head>
    <meta charset="utf-8">
    <title>Example of LoginView</title>
</head>
<body>
    <main>
        <h2>ログイン</h2>
        <form action="{% url 'login' %}" method="post">
            {% csrf_token %}
            <table>{{ form.as_table }}</table>
            <input type="hidden" name="redirect" value="{{ redirect }}">
            <p><input type="submit" value="送信"></p>
        </form>
    </main>
</body>
</html>

基本的には、単純にフォームを表示するだけのテンプレートファイルになるのですが、next フィールドを利用したリダイレクト で解説した、ログイン後の「ログイン前にアクセスしようとした URL」へのリダイレクトを実現するため、フォームに redirect フィールドを追加するようにしています。今回の例では、ビューでのクラス変数 redirect_field_name の定義によって、フィールド名やコンテキストのキー名が next から redirect に変化していることに注意してください。

user_form.html

続いて、UserRegistration が利用するテンプレートファイルとして user_form.html を用意します。先ほど作成した accounts フォルダの中に user_form.html を新規作成し、中身を下記のように変更してください。

user_form.html
<!doctype html>
<html lang="ja">
<head>
    <meta charset="utf-8">
    <title>Example of CreateView</title>
</head>
<body>
    <main>
        <h2>ユーザーの登録</h2>
        <form action="{% url 'register' %}" method="post">
            {% csrf_token %}
            <table>{{ form.as_table }}</table>
            <p><input type="submit" value="送信"></p>
        </form>
    </main>
</body>
</html>

user_list.html

最後に、UserList が利用するテンプレートファイルとして user_list.html を用意します。先ほど作成した accounts フォルダの中に user_list.html を新規作成し、中身を下記のように変更してください。

user_list.html
<!doctype html>
<html lang="ja">
<head>
    <meta charset="utf-8">
    <title>Example of DetailView</title>
</head>
<body>
    <main>
        <h2>ユーザー一覧</h2>
        <table>
            <thead>
                <tr>
                    <th>ID</th><th>ユーザー名</th>
                </tr>
            </thead>
            <tbody>
                {% for object in object_list %}
                <tr>
                    <td>{{ object.id }}</td>
                    <td>{{ object.username }}</td>
                </tr>
                {% endfor %}
            </tbody>
        </table>
        <form action="{% url 'logout' %}" method="post">
            {% csrf_token %}
            <p><input type="submit" value="ログアウト"></p>
        </form>
    </main>
</body>
</html>

このuser_list.html の最下部には ログアウト ボタンを設置しており、このボタンが押されることで UserLogout にマッピングされた URL へのリクエスト(メソッドは POST)が送信されてログアウトが実施されるようにしています。

下記ページで解説しているように、LogoutView のサブクラスによってログアウトを実施するためには、メソッドが POST のリクエストを送信する必要があるという点に注意してください。

LogoutViewの使い方の解説ページアイキャッチ 【Django】LogoutViewの使い方(クラスベースビューでのログアウトの実現)

リダイレクト先の設定

最後に、settings.py を変更してログイン後のリダイレクト先の URL とログアウト後のリダイレクト先の URL の指定を行なっていきます。

前者に関しては settings.pyLOGIN_REDIRECT_URL を定義することで、後者に関しては settings.pyLOGOUT_REDIRECT_URL を定義することで、それぞれを指定することが可能です。

MEMO

next フィールドを利用したリダイレクト で解説したように、ログイン後のリダイレクト先の URL に LOGIN_REDIRECT_URL が設定されるのは、フォームから送信されてきた redirect フィールドが空の場合のみとなります

今回は、下記を settings.py に追記することで2つのリダイレクト先の指定を行うようにしたいと思います。追記場所は settings.py の一番下で良いです。

リダイレクト先の指定
LOGIN_REDIRECT_URL = '/accounts/register/'
LOGOUT_REDIRECT_URL = '/accounts/login/'

これにより、クエリパラメーターで redirect が指定されていない場合はログイン後に /accounts/register/ にリダイレクトされてユーザーの新規登録ページ(新規登録フォーム)が表示されることになります。またログアウト後には /accounts/login/ にリダイレクトされてログインページ(ログインフォーム)が表示されることになります。

スポンサーリンク

動作確認

最後に、ここまで作成してきたアプリの動作確認を行なっていきたいと思います。

開発用ウェブサーバーの起動

まず manage.py が存在するフォルダで下記コマンドを実行します。

% python manage.py runserver

これにより、開発用ウェブサーバーが起動しますので、ウェブブラウザから作成したアプリのページを表示することが可能となります。

非ログインユーザーがアクセスできないことの確認

続いて下記 URL をウェブブラウザで表示してみてください。

http://localhost:8000/accounts/list/

この URL は、元々 urls.pyUserList にマッピングされている URL になります。なので、本来であれば UserList によってユーザーの一覧ページが表示されるはずですが、現状は非ログイン状態であるため強制的にリダイレクトが行われることになります。

そして、このリダイレクトは UserList が継承している LoginRequiredMixin によって実施され、リダイレクト先は settings.py で LOGIN_URL に指定される URL となります。ですが、今回は settings.pyLOGIN_URL は定義していないため、デフォルト設定の /accounts/login/ にリダイレクトされることになります。そして、この URL は UserLogin にマッピングされている URL であるため、結果としてログインページが表示されることになります。

ログインページが表示される様子

このように、LoginRequiredMixin を継承するビューを定義することで、非ログイン状態のユーザーのアクセスを拒否することが簡単に実現できます。

ユーザーの新規登録(ユーザーログイン)

現在ログインページが表示されているため、ここでログインを行えば先ほど表示しようとした /accounts/list/ も表示できるようになります。ですが、ログインを行うためには User のインスタンスの新規登録が必要です。そのために、次は下記 URL をウェブブラウザで開いてください。

http://localhost:8000/accounts/register/

すると、下図のようなユーザーの新規登録フォームが表示されるはずです。これは UserRegistration によって表示されるページとなります。

ユーザーの新規登録ページが表示される様子

このフォームでユーザー名とパスワード(さらには確認用のパスワード)を入力して 送信 ボタンをクリックしてください。パスワードは8文字以上などの制限があるので注意してください。この 送信 ボタンのクリックによってユーザーの新規登録が行われ、User のインスタンスがレコードとしてデータベースに保存されることになります。

また、UserRegistration では form_valid のオーバーライドによってレコードの保存後に login が実行されるようになっており、さらにクラス変数 success_url の定義によって名前が 'list' の URL、すなわち /accounts/list/ へのリダイレクトレスポンスが返却されるようになっています。

そのため、レコードの保存後にはユーザーがログイン状態となり、さらに /accounts/list/ にリダイレクトされてユーザー一覧が表示されるはずです(まだユーザーは一人しかいませんが…)。ポイントは、この URL は非ログイン状態のときはアクセスできませんでしたが、ログインが行われたことによってアクセス可能になったという点になります。

表示されるユーザー一覧のページ

ここまでの操作によって、LoginRequiredMixin を継承するクラスによって表示されるページが非ログイン状態の際には表示不可であること、さらにログイン中であれば通常通り表示可能であることが確認できたと思います。ログイン機能を搭載するのは非ログインユーザーからのアクセスを禁止することを目的とすることも多いため、この LoginRequiredMixin については覚えておくと良いと思います。

ログアウト

続いて、ユーザー一覧の最下部に設置された ログアウト ボタンをクリックしてください。これにより、UserLogout が動作してログアウトが実施されます。ログアウトが実施された後には、settings.pyLOGOUT_REDIRECT_URL/accounts/login/ を指定しているため、ログインページが表示されることになります。

ログアウト後にログインページが表示される様子

今回はログインは行わず、再度下記 URL をウェブブラウザで開いてみてください。

http://localhost:8000/accounts/list/

すると、最初に上記 URL をウェブブラウザで表示した時と同様にログインページにリダイレクトされるはずです。これは、先ほどの ログアウト ボタンのクリックによってログアウトが実行され、非ログイン状態に戻ってしまったからになります。前述の通り、このリダイレクトは LoginRequiredMixin によって実施されます。

ログイン(クエリパラメーター redirect 有りの場合)

ここまで説明した手順を実施してくださった方であれば、現在ログインページが表示されているはずです。

ここで注目していただきたいのがリダイレクトされた先の URL となります。ということで、ウェブブラウザのアドレスバーを確認してみてください。アドレスバーに表示される URL は下記のようなものになっており、クエリパラメーター redirect が指定されていることが確認できると思います。

リダイレクト先のURL

このようにリダイレクト先の URL にクエリパラメーター redirect が追加されるようになっているのは UserListLoginRequiredMixin を継承しており、さらにクラス変数 redirect_field_name'redirect' を指定しているからになります。そして、クエリパラメーターの redirect の値は /accounts/list/ であり、これは非ログイン状態でアクセスしようとしたページの URL のルートパスになります。

また、表示されているログインページのソースを確認してみれば分かると思いますが、フォーム要素部分の HTML は下記のようになっており、name="redirect" および value="/accounts/list/" が指定された input タグが存在することになります。そのため、送信 ボタンがクリックされた際には "redirect" : "/accounts/list/" という redirect フィールドを含むデータが送信されることになります。

フォーム要素のHTML
<form action="/accounts/login/" method="post">
    <input type="hidden" name="csrfmiddlewaretoken" value="略">
〜略〜
    <input type="hidden" name="redirect" value="/accounts/list/">
〜略〜

ということで、次は実際にログインを行なってみましょう!

先ほどユーザーを新規登録したはずですので、その登録した usernamepassword をフォームの各フィールドに入力して 送信 ボタンをクリックしてください。

usernamepassword が正しければ、ログインが行われた後に自動的にユーザー一覧の表示ページに遷移するはずです。

表示されるユーザー一覧のページ

このページは非ログインユーザーからはアクセス不可であるため、このページが表示されたことでログインが正常に実施されたことが確認できたことになります。また、このページに遷移したのは next フィールドを利用したリダイレクト で説明した ログイン前にアクセスしようとした URL へのリダイレクトが上手く実現できているからで、具体的には「クエリパラメーター redirect で指定された /accounts/list/」を値とする redirect フィールドがログインフォームから送信されたために /accounts/list/ へのリダイレクトが行われたことになります。

フォームから送信されたデータにredirectフィールドが存在することを示す図

ログイン(クエリパラメーター redirect 無しの場合)

次は、クエリパラメーターが存在しない場合のログイン後のリダイレクトについて確認してみましょう!この場合は、settings.pyLOGIN_REDIRECT_URL で指定された URL へのリダイレクトが行われることになります。

再度、ユーザー一覧ページに設置された ログアウト ボタンをクリックしてログインを実施してください。今回も、ログアウト後には自動的に下記 URL へリダイレクトされることになります。

http://localhost:8000/accounts/login/

そして、この場合は LoginRequiredMixin によるリダイレクトが行われたわけではないので、リダイレクト先の URL にはクエリパラメーターが存在しないことになります。このように、直接ログインページを開かれた際にはクエリパラメーターは付加されません。また、クエリパラメーターが存在しないことは、ウェブブラウザのアドレスバーに表示される URL からも確認できると思います。

ログインページに直接アクセスした場合のURL

続いて、先ほどと同様に usernamepassword を入力して 送信 ボタンをクリックしてください。今度はログイン後に下記 URL にリダイレクトされてユーザーの新規登録ページに自動的に遷移するはずです。先ほどログインを実施したときとは異なるページへのリダイレクトが行われている点がポイントになります。

http://localhost:8000/accounts/register/

そして、この違いはクエリパラメーター redirectの有無によるものとなります。前述の通り、URL にクエリパラメーター redirect が存在する場合は redirect で指定される URL にリダイレクトされますが、存在しない場合は settings.pyLOGIN_REDIRECT_URL にリダイレクトされることになります。今回、LOGIN_REDIRECT_URL には /accounts/register/ を指定しているため、上記 URL にリダイレクトされたことになります。

ログイン状態でのログインページの表示

最後に、ログイン状態でログインページを表示しようとした時の動作について確認しておきたいと思います。

現在はログイン状態のはずなので、その状態で再度下記 URL をウェブブラウザで開いてみてください。

http://localhost:8000/accounts/login/

すると、ログインページが表示されるのではなく、またユーザーの新規登録ページが表示されることになるはずです。これは、UserLogin でクラス変数 redirect_authenticated_user を定義して True を指定しているためで、この場合はログイン状態のユーザーがログインページを表示しようとすると他の URL へのリダイレクトが行われることになります。そして、このリダイレクト先は、ログイン後のリダイレクト先の URL と同じ URL となります。先ほど開いた URL にはクエリパラメーター redirect が存在しないため、settings.py の LOGIN_REDIRECT_URL で指定された URL、すなわち /accounts/register/ がリダイレクト先の URL となりますので、ユーザーの新規登録ページが表示されたことになります。

ログイン状態でもログインページを表示できるようにしたいのであれば、クラス変数 redirect_authenticated_user の定義を削除してやれば良いことになります。

以上の動作確認により、開発したウェブアプリでログインやログアウトが実施可能であること、さらに設定どおりのリダイレクトが実現できていることが確認できたと思います!

まとめ

このページでは、LoginView および LoginView を継承したクラスベースビューの作り方について解説しました!

LoginView はログインを実現するための View のサブクラス であり、このクラスを継承するビューを定義することで、ウェブアプリにログイン機能を搭載することが可能となります。

LoginViewFormView のサブクラスであり、既にログインに特化する形で作り込まれた View のサブクラス となります。したがって、他の View のサブクラス に比べるとカスタマイズ要素は少なめです。

そのため、ログインは LoginView を利用することで簡単にログインを実現することが可能です。LoginView のサブクラスの定義を行うだけでほぼ出来上がりです。ただし、他のビューにアクセス制限を設ける必要があったり、リダイレクトに関してはややこしい点もあったりしますので、この辺りがウェブアプリにログイン機能を搭載する際のポイントになると思います。

様々なウェブアプリでログイン機能が搭載されていることからも分かるように、ログイン機能が実現できるようになれば、開発可能なウェブアプリの幅が一気に広がります。その分ウェブアプリ開発も楽しくなると思いますので、是非 LoginView を利用したビューの作り方については覚えておいてください!

また、このサイトでは他の View のサブクラス についても説明していますので、他のページも是非読んでみてください!

DjangoのListViewの解説ページアイキャッチ 【Django】ListViewの使い方(クラスベースビューでの一覧リストページの実現) DjangoのDetailViewの解説ページアイキャッチ 【Django】DetailViewの使い方(クラスベースビューでの詳細ページの実現) CreateViewでのクラスベースビューの実現方法解説ページアイキャッチ 【Django】CreateViewの使い方(クラスベースビューでの新規登録ページの実現) UpdateViewでのクラスベースビューの実現方法解説ページアイキャッチ 【Django】UpdateViewの使い方(クラスベースビューでのレコード更新ページの実現) DeleteViewでのクラスベースビューの実現方法解説ページアイキャッチ 【Django】DeleteViewの使い方(クラスベースビューでのレコード削除ページの実現) FormViewでのクラスベースビューの実現方法解説ページアイキャッチ 【Django】FormViewの使い方(クラスベースビューで汎用的なフォームを扱う) LogoutViewの使い方の解説ページアイキャッチ 【Django】LogoutViewの使い方(クラスベースビューでのログアウトの実現)

同じカテゴリのページ一覧を表示