このページでは LoginView
の使い方について説明していきます。
この LoginView
は View
というクラスのサブクラスの1つであり、ビューをクラスベースで作成する際に利用するクラスとなります。この View のサブクラス
を継承し、さらにクラス変数を定義したりメソッドをオーバーライドすることで、あなたが開発したいアプリに応じたビューを作成することが可能となります。
この辺りのクラスベースビューやクラスベースビューの作り方については下記ページで解説していますので、詳しくはこちらをご参照ください。
【Django入門15】クラスベースビューの基本このページで目指すことは、LoginView
を利用し、上記ページで紹介しているログイン機能を持つウェブアプリに必要な動作や処理をクラスベースビューで実現していくことになります。
Contents
LoginView
まずは、LoginView
がどういったクラスであるのかについて解説していきたいと思います。
名前の通り、LoginView
はログインを実現するクラスとなります。
auth
アプリから提供されるクラス
このサイトでは、今まで View のサブクラス
として CreateView
や UpdateView
等を紹介してきましたが、これらは django.views.generic
から提供されるクラスでした。そのため、これらは django.views.generic
から import
して利用する必要がありました。
それに対し、LoginView
は django.contrib.auth.views
から import
して利用することになります。
from django.contrib.auth.views import LoginView
LoginView
は auth
というアプリで定義されるクラスとなりますので、上記のように import
の仕方が他の View のサブクラス
とは異なる点に注意してください。
また、auth
アプリでは、下記のように認証やログイン等を実現するためのクラスや関数等が多数定義されており、LoginView
は、下記のクラスや関数を利用してログインが実施されるように実装されたクラスとなります。
- ユーザーを管理するモデルクラス;
User
(カスタムユーザー
) - ユーザー登録用のフォームクラス;
UserCreationForm
- ログイン用のフォームクラス:
AuthenticationForm
- 認証を行う関数:
authenticate
- ログインを行う関数:
login
関数ベースビューの場合、上記のようなクラスや関数を利用して開発者自身がログインが実現できるような処理を実装する必要があるのですが、クラスベースビューの場合は、LoginView
を継承するクラスを定義し、必要なカスタマイズを行うだけでログインが実現できることになります。
関数ベースビューでログインを実現する手順等については、下記ページで解説していますので、興味があれば読んでみていただければと思います。
【Django入門10】ログイン機能の実現スポンサーリンク
LoginView
の処理の流れ
続いて LoginView
の処理の流れについて説明しておきます。LoginView
では、GET
リクエストを受け取った際のログインページの表示と、POST
リクエストを受け取った際の認証・ログイン処理によってログイン機能が実現されるようになっています。
LoginView
は、まず GET
リクエストを受け取った際にログイン用のフォームを含むログインページの表示を行います。このログイン用のフォームとは、先ほども紹介した AuthenticationForm
となります。この AuthenticationForm
ではユーザー名を入力する username
フィールドとパスワードを入力する password
フィールドが定義されており、ログインページ表示時にはこれらのフィールドを持つログイン用のフォームが表示されることになります。
このフォームから username
フィールドと password
フィールドを含むデータが POST
リクエストとして送信されてきた際、LoginView
は username
フィールドの値(ユーザー名)と password
フィールドの値(パスワード)に基づいて認証を実施します。この認証では、入力されたユーザー名に対するパスワードが正しいかどうかの判断が行われます。また、この認証は AuthenticationForm
のインスタンスから is_valid
メソッドを実行させることで行われます(より細かくいうと、is_valid
メソッドから authenticate
という関数が実行されて認証が行われることになります)。
そして、認証が OK の場合のみ、username
フィールドに入力されたユーザー名のユーザーに対するログイン処理を実施します。このログイン処理は login
関数の実行によって行われます。さらに、ログイン処理の後に特定の URL に対するリダイレクトレスポンスを返却します。
認証が NG の場合、例えば入力されたユーザー名のユーザーが登録されていない、入力されたパスワードが登録されているものと一致しないような場合は、再度ログイン用のフォームが表示されることになります。
このように、LoginView
はログインを行うために必要となる “ログイン用のフォームの表示” や “認証処理”・”ログイン処理” が行われるように実装されているビューとなります。
LoginView
と AUTH_USER_MODEL
この LoginView
と密接に関連するのが settings.py
で定義する AUTH_USER_MODEL
になります。
まず、LoginView
で実施されるログインとは、ユーザーをログイン状態に移行するための処理となります。そして、この『ユーザー』とは、具体的には settings.py
で定義した AUTH_USER_MODEL
に指定されたモデルクラスのインスタンスとなります。settings.py
で AUTH_USER_MODEL
が定義されていない場合は、AUTH_USER_MODEL
にはデフォルトの User
というモデルクラスが適用され、LoginView
では、この User
のインスタンスに対してログインが実施されることになります。
そのため、他のモデルクラス、例えば下記ページで解説している カスタムユーザー
のインスタンスをユーザーとしてログインを実施したいような場合は、settings.py
での AUTH_USER_MODEL
の定義が必要となります。
また、先ほど LoginView
では認証が実施されると説明しましたが、この認証は、フォームから送信されてきた username
フィールドの値および password
フィールドの値と同じフィールドの値を持つレコードがデータベースのテーブルに存在するかどうかの確認であり、存在する場合は認証 OK と判断されます。そして、この『テーブル』とは、AUTH_USER_MODEL
に指定されたモデルクラスに対応するテーブルとなります(Django ではモデルクラスがデータベースのテーブルとして扱われます)。
したがって、認証で OK と判断されるためには、事前に AUTH_USER_MODEL
のテーブルにレコードを新規登録しておく必要があります。このテーブルにレコードが存在しなければ必ず認証が NG となります。なので、ログインを実現するためには AUTH_USER_MODEL
のテーブルにレコードを新規登録するビューも必要となります。
ちなみに、このようなレコードの新規登録を行うビューは、下記ページで解説している CreateView
を継承することで簡単に実現可能です。
このように、LoginView
は AUTH_USER_MODEL
のテーブルや、AUTH_USER_MODEL
のインスタンスを利用して動作するようになっています。この点についてはしっかり覚えておいてください。
LoginView
でのクラスベースビューの作り方
では、次は LoginView
でクラスベースビューを作成する手順について説明していきます。
LoginView
でのクラスベースビューの作り方に関しても基本的には他の View のサブクラス
と同じです。
つまり、views.py
に LoginView
を継承するクラスを定義し、そのクラスでクラス変数やメソッドを定義して LoginView
側で定義されているクラス変数の上書き・メソッドのオーバーライドを行なうことで、ビューを作成していきます。これにより、LoginView
の特徴を活かしながら自身のウェブアプリに応じたビューにカスタマイズしていくことが可能となります。
また、LoginView
ではリクエストに応じて異なる処理が行われるようになっており、具体的には GET
リクエストを受け取った際には get
メソッドが、POST
リクエストを受け取った際には post
メソッドが実行されるようになっています。そして、LoginView
のクラス変数や他のメソッドは、これらの get
メソッドおよび post
メソッドから利用されるようになっています。したがって、LoginView
のサブクラス側でクラス変数やメソッドを上書き・オーバーライドしてやれば、この get
メソッドと post
メソッドの動作を変化させることができます。
スポンサーリンク
最低限のカスタマイズで利用可能
CreateView
や FormView
等の他の View のサブクラス
は基本的に開発者がカスタマイズすることを前提として用意されているビューとなります。それに対し、LoginView
は FormView
をカスタマイズすることで作られたビューで、LoginView
はログインに特化する形で既に作り込まれたビューとなります。
そのため、LoginView
に関しては最低限のカスタマイズのみで利用可能なビューとなっています。
例えば、CreateView
のサブクラスではクラス変数 form_class
(or model
+ fields
) の定義を行なってビューで扱うモデルフォームクラスを必ず指定する必要がありました。これは、CreateView
がどんなモデルフォームクラスでも対応できるような汎用的な作りになっているからになります。
それに対し、LoginView
に関してはログインに特化しているため、あらかじめログイン用フォームとして form_class
に AuthenticationForm
が指定されるようになっています。そのため、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
というデコレーターを利用することになります。これに関しての詳細は下記ページを参照してください。
LoginRequiredMixin
によるリダイレクト
また、上記のようなアクセス制限はリダイレクトによって実現されることになります。
つまり、LoginRequiredMixin
を継承したビューに対して非ログインユーザーがリクエストを送信してきた場合、ビューのメソッドが実行されるのではなく、リダイレクトレスポンスが返却されることになります。そして、このリダイレクトレスポンスを受け取ったウェブブラウザは、そのリダイレクト先の URL へのリクエスト(メソッドは GET
)をウェブアプリに対して送信することになり、それによって元々アクセスしようとしていたページとは異なるページが表示されることになります。
このリダイレクト先の URL は下記となります。
LOGIN_URL/?next=ログイン前にアクセスしようとしたページのURL
この LOGIN_URL
の具体的な URL に関しては settings.py
で LOGIN_URL
を定義することで設定可能です。また、この LOGIN_URL
にはログインページの URL を設定しておくことが一般的で、これにより、非ログインユーザーが LoginRequiredMixin
にアクセスを拒否された際にはログインページにリダイレクトされることになり、ユーザーがわざわざログインページを開く手間を省くことができます。
例えば、アプリのログインページの URL が /forum/login/
であり、さらに非ログインユーザーの /forum/comments/
の URL へのアクセスが LoginRequiredMixin
によって制限されているとしましょう。この場合、settings.py
で LOGIN_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 = '/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
の定義が必要となります。例えば、アプリ名 forum
の models.py
に定義した CustomUser
というモデルクラスを利用させるようにするためには、下記のように settings.py
に AUTH_USER_MODEL
を定義してやれば良いです。
AUTH_USER_MODEL = 'forum.CustomUser'
ただし、AUTH_USER_MODEL
に指定するモデルクラスはログインや認証に利用されるため、ユーザーを識別するフィールドやパスワードを管理するフィールドも必要となりますし、そのパスワードを暗号化するメソッド等も必要になります。なので、基本的には AUTH_USER_MODEL
には AbstractUser
や AbstractBaseUser
というモデルクラスのサブクラスとして定義した カスタムユーザー
を指定する必要があります。この カスタムユーザー
については下記ページで解説していますので、詳細は下記ページを参照してください。
ログイン後のリダイレクト先の設定
また、LoginView
では、メソッドが POST
のリクエストを受け取ってログインを行なった後にはリダイレクトレスポンスの返却が行われるようになっています。LoginView
を利用してログインを実現する場合、このリダイレクトレスポンスにおけるリダイレクト先の URL の設定も必要となります。
LoginView
においては、リダイレクト先の URL は下記3つの方法で設定可能です。下記の内の複数で URL が設定されている場合、上から順に優先してリダイレクト先の URL に設定されることになります。
- ログインフォームから送信するデータの
next
フィールドで指定する LoginView
のサブクラスのクラス変数next_page
で指定する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入門5】フォームの基本また、このテンプレートファイルの作り方を工夫することで、ログイン後の「ログイン前にアクセスしようとした URL
へのリダイレクト」を実現することが可能です。これに関しては、後述の next フィールドを利用したリダイレクト で詳細を解説します。
スポンサーリンク
ログアウト用のビューを定義する
また、ログインを実現するのであればログアウトも必要となります。
ログアウトに関しては下記ページで解説している LogoutView
を継承するサブクラスで実現可能です。LogoutView
でログインを実現する例は、最後の LoginView の利用例 で示します。
LoginView
のクラス変数
では、次は LoginView
で定義されているクラス変数の紹介を行なっていきます。これらのクラス変数を LoginView
を継承するクラスで定義し直して上書きすることでクラスの動作のカスタマイズを行うことが可能となります。
LoginView
の場合、GET
メソッドのリクエストを受け取った際に get
メソッドが実行され、POST
メソッドのリクエストを受け取った際に post
メソッドが実行されることになるため、これらの get
メソッドや post
メソッドの動作を変化させることを目的にクラス変数の定義を行なっていくことになります。
LoginView
のクラス変数の一覧
この LoginView
で定義されるクラス変数には下記のようなものが存在します。
- form_class
- authentication_form
- next_page
- redirect_field_name
- redirect_authenticated_user
- template_name
- extra_context (
ListView
) - initial (
CreateView
) - prefix (
CreateView
) - template_engine (
ListView
) - response_class (
ListView
) - content_type (
ListView
)
後半に示したクラス変数に関しては、他の View のサブクラス
の解説ページで既に説明済みです。そのため、各クラス変数のテキストには、それらのページへのリンクを貼っています。他のページにジャンプするリンクテキストの横には括弧内でジャンプ先のページを示していますので、これらのクラス変数の詳細を知りたい方はリンク先の説明を参照していただければと思います。
ここでは、上記の前半部分に示した、特に LoginView
ならではの特徴を持つクラス変数について説明していきます。
スポンサーリンク
form_class
form_class
は LoginView
のサブクラスで扱うフォームクラスを指定するクラス変数になります。CreateView
や UpdateView
、さらには FormView
等も同じクラス変数を持っていますが、LoginView
の場合はログイン用(認証用)のフォームクラスを指定する必要があります。ここが他の View のサブクラス
とは異なります。
また、LoginView
の場合、クラス変数 form_class
を定義しなくても、デフォルトで form_class
に AuthenticationForm
が指定されるようになっているため、form_class
を定義しなくても自動的にLoginView
が利用するフォームクラスが設定されるようになっています。
そして、この AuthenticationForm
はユーザー名とパスワードの入力フィールドが定義されたクラスになります。このクラスのインスタンスに is_valid
メソッドを実行させればパスワード認証が実行されます(is_valid
メソッドは LoginView
が POST
リクエストを受け取った際、ユーザーが入力したユーザー名やパスワードに対して実施されるようになっています)。このように、AuthenticationForm
はログイン用フォームとして必要なフィールドや機能を備えており、基本的には form_class
は定義せず、デフォルトの 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 へのリダイレクトレスポンスが返却されることになります。
class UserLogin(LoginView):
next_page = 'top-page'
LoginView
では、前述の通り、ログイン後のリダイレクト先の URL は下記の3つの方法で設定可能です。複数の方法で設定された場合は、上側の設定が優先して採用されることになります。
- ログインフォームから送信するデータの
next
フィールドで指定する LoginView
のサブクラスのクラス変数next_page
で指定する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_user
が False
の場合はそのままログインページを表示されることになります。
それに対し、 redirect_authenticated_user
を True
に指定しておくと、ログイン済みのユーザーが LoginView
のサブクラスが表示するページにアクセスした際に、特定の URL へのリダイレクトレスポンスが返却されるようになります。つまり、ログイン状態のユーザーはログインページを表示できなくなります。既にログイン済みのユーザーにログインページを表示する必要もないので、他のページにリダイレクトするというわけです。
また、このリダイレクト先は、ログイン後のリダイレクト先の URL と同じ URL となります。
template_name
template_name
は、他の View のサブクラス
同様に LoginView
のサブクラスが利用するテンプレートファイルを指定するクラス変数となります。デフォルトは 'registration/login.html'
となります。また、このテンプレートファイルは、メソッドが GET
のリクエストを受け取った時にログインフォームを表示するために利用されることになります。
また、下記ページで解説している通り、LogoutView
が利用するデフォルトのテンプレートファイルのパスは 'registration/logged_out.html'
で、このパスには Django フレームワークに用意されたアプリの admin
によって用意されたテンプレートファイルが存在します。なので、LogoutView
が利用するテンプレートファイルは用意しなくても、'registration/logged_out.html'
を利用してログアウト後のページ表示が可能となります。
ですが、LoginView
のデフォルトのテンプレートファイルのパスである 'registration/login.html'
にはファイルが存在しませんので、別途このパスにテンプレートファイルを設置する or クラス変数 template_name
を定義し、この template_name
に指定したパスに自身で作成したテンプレートファイルを設置する必要があります。この点が LogoutView
と異なりますので注意してください。
スポンサーリンク
LoginView
のメソッド
次に LoginView
の持つメソッドを紹介していきます。LoginView
を継承したクラスを定義し、そのクラスで LoginView
の持つメソッドをオーバーライドしてやることで LoginView
とは異なる動作のクラスを実現することができるようになります。
前述でも説明しましたが、LoginView
はログイン用のビューとして既に作り込まれているため、メソッドのオーバーライドを行う機会は少ないのではないかと思います。
ということで、ここではオーバーライドの例などは示さず、LoginView
のメソッドの一覧のみを示しておきたいと思います。
LoginView
のメソッド一覧
LoginView
の持つメソッドの一覧は下記のようになります。あくまでもクラスのカスタマイズ目的でオーバーライドを行う可能性のあるものを挙げており、as_view
などのカスタマイズは行わないであろうメソッドは省略しています。
dispatch
:リクエストのメソッドに応じてget
orpost
を実行する、もしくはリダイレクトレスポンスを返却するget
:リクエストのメソッドがGET
の場合の処理を実行する(ログインフォームを表示する)post
:リクエストのメソッドがPOST
の場合の処理を実行する(ログインとリダイレクトレスポンスの返却を行う)get_context_data
:テンプレートに渡すコンテキストを生成するget_form
:フォームクラスのインスタンスを取得するget_form_class
:フォームクラスを取得するget_form_kwargs
:フォームクラスのコンストラクタに指定するパラメータを取得するget_initial
:initial
への指定値を取得するget_prefix
:prefix
への指定値を取得するform_valid
:ログインを実行し、その後に特定の URL へのリダイレクトレスポンスを返却する(妥当性の検証が OK の時に実行される)form_invalid
:再度フォームを表示する(妥当性の検証が NG の時に実行される)get_success_url
:リダイレクト先の URL を取得するget_redirect_url
:非ログイン状態でアクセスしようとしていた URL を取得するget_default_redirect_url
:next_page
orLOGIN_REDIRECT_URL
で指定される URL を取得するget_template_names
:テンプレートファイルの名前を取得するrender_to_response
:レスポンスを返却する
ポイントとなるメソッドの1つが dispatch
となります。リクエストを受け取った際に最初に実行される LoginView
のメソッドは dispatch
となり、クラス変数 redirect_authenticated_user
が True
の場合、アクセスしてきたユーザーが既にログイン中であればリダイレクトが行われることになります。
それ以外の場合は、リクエストのメソッドの種類(GET
or POST
)に応じて get
or post
が dispatch
から実行されるようになっています。そして、これらの get
や post
の中から上記の各種メソッドが実行されるようになっています。
また、ログイン後のリダイレクト先の URL は get_success_url
メソッドが実行されることで決定されることになります。そのため、この get_success_url
メソッドをオーバーライドすることで、下記の3つ以外の方法でリダイレクト先の URL を設定することも可能となります。
- ログインフォームから送信するデータの
next
フィールドで指定する LoginView
のサブクラスのクラス変数next_page
で指定する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
にリダイレクトされるようなものが多いです。
Django で開発するウェブアプリにおいても、next
フィールドを利用したリダイレクトにより、このような、ログアウト後の「ログイン前にアクセスしようとした URL
へのリダイレクト」を実現することができます。
ログイン前にアクセスしようとした URL
へのリダイレクトの実現方法
この実現方法の具体的な手順を、ここで説明していきます。
フォームから next
フィールドを含んだデータを送信させることが必要
まず、ログイン後のリダイレクト でも説明したように、LoginView
でのログイン後のリダイレクト先の URL は下記の3つの方法で設定することができます。
- ログインフォームから送信するデータの
next
フィールドで指定する LoginView
のサブクラスのクラス変数next_page
で指定するsettings.py
のLOGIN_REDIRECT_URL
で指定する
ログイン前にアクセスしようとした URL
へのリダイレクトは、上記の 1. によって実現することになります。
上記のようにリダイレクト先の URL が設定できるようになっているため、ログインフォームから next
フィールドを含むデータを送信できるようにし、さらに、その next
フィールドの値に ログイン前にアクセスしようとした URL
がセットされるようになれば、後は LoginView
がログイン後に ログイン前にアクセスしようとした URL
にリダイレクトしてくれることになります。
問題は、このログインフォームから送信されるデータの next
フィールドの値に、どうやって ログイン前にアクセスしようとした URL
をセットするのか?という点になります。
この点について考えていきましょう!
コンテキストには ログイン前にアクセスしようとした URL
が含まれる
まず、ここで LoginRequiredMixin によるリダイレクト の節での解説内容を思い出してみましょう。クラスベースビューのビューに LoginRequiredMixin
を継承させれば、そのビューによって表示されるページに非ログインユーザーがアクセスするとリダイレクトレスポンスの返却が行われるようになるのでしたね!
そして、そのリダイレクト先の URL は下記となります。
LOGIN_URL/?next=ログイン前にアクセスしようとしたページのURL
リダイレクトレスポンスを受け取ったクライアントは、このリダイレクト先の URL に対するメソッド GET
のリクエストを送信することになります。また、上記における LOGIN_URL
は settings.py
で定義することが可能で、通常ログインページの URL が指定されることになります。したがって、ログインを LoginView
のサブクラスで実現している場合、LoginRequiredMixin
によるリダイレクトによって、上記の URL & メソッド GET
のリクエストを LoginView
のサブクラスが受け取ることになります。
これにより、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
を継承させておけば、そのアクセスの拒否によってログインページにリダイレクトされて LoginView
の get
メソッドが実行された際には、next
キーの値に ログイン前にアクセスしようとした URL
がセットされたコンテキストが生成されるということになります。
next
フィールドが送信されるフォームを作成する
また、コンテキストにセットされている値はテンプレートファイルに埋め込むことが可能です。また、テンプレートファイルは開発者が自由に作成することができます。当然、開発者の好きなようにフォーム要素を定義することも可能です。
なので、コンテキストに ログイン前にアクセスしようとした URL
がセットされているのであれば、ログインフォームに対して、値が ログイン前にアクセスしようとした URL
である next
フィールドを追加することも可能です。これによって、元々実現したかった「値が ログイン前にアクセスしようとした URL
である next
フィールドを含むデータのフォームからの送信」が実現できることになります。ただ、この next
フィールドはユーザーから入力受付を行うものではないので非表示にしてやるのが良いと思います。
具体的には、このようなフォームは、テンプレートファイルに下記のような form
タグを記述することで実現できます。テンプレートファイルを用意する で示したテンプレートファイルに対し、name
が next
の input
タグを追加しています。これが 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 }}
によって出力されるフィールド(username
と password
)に追加して、next
フィールドの値もボタン要素クリック時に送信されるようになります。
また、この next
フィールドには type="hidden"
が指定されているためページには表示されません。なので、ユーザーから値は基本的には変更不可で、送信される next
フィールドの値はコンテキストの next
キーの値、すなわち ログイン前にアクセスしようとした URL
となります。
さらに、form
タグには上記のように action
属性と method
属性が指定されていますので、ボタンクリック時には URL が ログインページのURL
でメソッドが POST
のリクエストが送信されることになります。各種フィールドは、そのリクエストのボディとして送信されます。
で、このリクエストをログインを LoginView
のサブクラスで実現しているウェブアプリが受け取れば、LoginView
のサブクラスの post
メソッドが実行されることになり、ログイン実施後にリダイレクトレスポンスの返却が行われることになります。そして、そのリダイレクト先の URL には、フォームから送信されてきたデータに next
フィールドが存在するため、その next
フィールドの値、すなわち ログイン前にアクセスしようとした URL
が設定されることになります。つまり、ログイン後の「ログイン前にアクセスしようとした URL
」へのリダイレクトが実現できることになります。
説明が長くなりましたが、結論としては、LoginView
のサブクラスでログインを実現する場合、ログイン後の ログイン前にアクセスしようとした URL
へのリダイレクトを実現するために必要な作業は下記の2つのみとなります。
- アクセス制限したいビューに
LoginRequiredMixin
を継承させる - ログインフォームを表示するテンプレートファイルに下記の属性を指定した
input
タグを記述するname="next"
value="{{ next }}"
上記の2つの両方を行えば、後は LoginView
が、ログイン後に ログイン前にアクセスしようとした URL
へリダイレクトされるよう動作してくれます。
逆に、ログイン後の ログイン前にアクセスしようとした URL
へのリダイレクトを行いたくない場合は、テンプレートファイルへの上記のような name
が next
の input
タグの記述をやめてしまえば良いです。これによってフォームから送信されるデータには next
フィールドが存在しなくなるため、next
フィールドによるリダイレクト先の URL の設定が行われなくなります。
スポンサーリンク
next
フィールドを利用したリダイレクトの注意点
ここまで説明してきた通り、LoginView
ではフォームから送信されてくるデータの next
フィールドをリダイレクト先の URL に設定することが可能です。
ただし、この next
フィールドを利用したリダイレクトにはいくつか注意点が存在しますので、その点について説明しておきます。
next
フィールドは空の場合がある
まず、フォームから next
フィールドを含むデータを送信できるようにしたとしても、next
フィールドが空になる場合があるという点に注意してください。
URL にクエリパラメーター next
が自動的に追加されるのは LoginRequiredMixin
を継承するビューに非ログインユーザーがアクセスしてリダイレクトされた時となります。したがって、これ以外の場合は URL にクエリパラメーター next
が自動的に追加されないことになります。例えば、ログインページを表示するビューは、非ログインユーザーからもアクセスできるようにするため LoginRequiredMixin
を継承してはいけません。そのため、非ログインユーザーでも直接ログインページにアクセスすることが可能で、この場合はクエリパラメーター next
が追加されないことになります。
クエリパラメーター next
が存在しない場合、前述のようにログイン後に ログイン前にアクセスしようとした URL
へリダイレクトされるようにアプリを作成したとしても、フォームから送信される next
フィールドの値は空となります。この場合、下記の 1. によるリダイレクト先の URL の設定が行われないことになります。
- ログインフォームから送信されるデータの
next
フィールドでリダイレクト先の URL を設定する LoginView
のサブクラスでクラス変数next_page
を定義するsettings.py
でLOGIN_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
” という名称のクエリパラメーター・キー・フィールドが利用されることになります。
- クエリパラメーター
next
で指定された URL がLoginView
の生成するコンテキストのnext
キーに設定される - フォームから送信されてきた
next
フィールドの値がリダイレクト先の URL に設定される
また、LoginView
のサブクラスではクラス変数 redirect_field_name
が定義可能で、このクラス変数 redirect_field_name
の定義によって、上記における “next
” という名称が変更可能となっています。
ただし、このクラス変数 redirect_field_name
を定義すると、その定義に合わせて LoginView
のサブクラス以外の変更も必要になるという点に注意してください。
まず、テンプレートファイルの変更が必要となります。例えば先ほど示した下記のフォーム要素は、{{ next }}
によってコンテキストの next
キーの値を参照するようになっています。さらに、name="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 として扱うフィールド名が変化することになるため、それに合わせてテンプレートファイルの変更も必要となります。今回の場合であれば、下記のようにテンプレートファイルのフォーム要素を変更する必要があります。
<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
を定義すると、その参照先のクエリパラメーターの変数名が変化することになります。
なので、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
がプロジェクトに登録されることになります。
INSTALLED_APPS = [
'accounts', # 追加
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
スポンサーリンク
フォームの定義とモデルの定義は省略
今回はフォームの定義とモデルの定義は行わず、auth
アプリで定義される AuthenticationForm
を LoginView
のサブクラスが扱うフォームクラスとし、さらにユーザーを管理するモデルクラスとして User
を利用します。これらは認証やログイン等の機能を備えたクラスとなります。
もし LoginView
のサブクラスが扱うフォームクラスを他のものにしたいのであれば、form_class や authentication_form で説明したように、これらのクラス変数を定義することで変更することも可能です。
また、ユーザーを管理するモデルクラスを User
から変更したい場合は カスタムユーザー
を別途定義し、それを利用するようにする必要があります。というか、そもそもユーザーを管理するモデルクラスとしては User
をそのまま利用することは推奨されていません(お試しで開発するウェブアプリで利用することは問題ないです)。この辺りに関しては下記ページで解説していますので、必要に応じてご参照していただければと思います。
今回はお試しのウェブアプリを開発していくだけなので、User
をそのまま利用していきたいと思います。
マイグレーションの実施
今回は新たにモデルの定義は行いませんが、データベース内にテーブルは必要なので、そのテーブルの作成を行うためにマイグレーションを実施します。具体的には、今回のウェブアプリの場合、User
のインスタンスをレコードとして保存するためのテーブルや、セッション情報のレコードを保存するためのテーブルが必要となります。
ただし、今回は models.py
でのモデルクラスの定義は行っていないため、必要なコマンドは下記の1つのみとなります。
% python manage.py migrate
クラスベースビューの作成
続いて、本題のクラスベースビューの作成を行っていきます。今回は、ここまでの説明の通り LoginView
のサブクラスを定義し、そのクラスをクラス変数の定義によってカスタマイズしていきます。また、ログアウトを実現するためのビューとして LogoutView
のサブクラスの定義も行います。さらに、非ログインユーザーがアクセス不可なページを実現するビューとして ListView
のサブクラス、ユーザーの新規登録を実現するビューとして CreateView
のサブクラスを定義していきます。
今回は、下記のように views.py
を変更し、LoginView
のサブクラスとして UserLogin
を定義したいと思います。また、動作確認を目的に、LogoutView
のサブクラスとして UserLogout
、ListView
のサブクラスとして UserList
、CreateView
のサブクラスとして UserRegistration
も定義しています。
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
においてもログアウト実施後にリダイレクトが行われることになります。UserLogout
においてもクラス変数 next_page
を定義していないため、settings.py
での LOGOUT_REDIRECT_URL
の定義が必要となります。この変更についても後ほど説明します。
UserRegistration
UserRegistration
は CreateView
のサブクラスであり、ユーザーの新規登録を実現するビューとなります。より具体的には User
のテーブルにインスタンス(レコード)を新規登録するビューとなります。
この CreateView
に関しては下記ページで詳細を解説していますので、各クラス変数やメソッドの意味合いに関しては下記ページをご参照いただければと思います。
ログインを実現する上での、ユーザーの新規登録を行うビューを定義するときの注意点は、そのインスタンスの新規登録先のテーブルが AUTH_USER_MODEL
となるように定義する必要がある点になります。今回は settings.py
での AUTH_USER_MODEL
の定義は行わないため、この AUTH_USER_MODEL
は User
となります。したがって、UserRegistration
は、インスタンス(レコード)を User
のテーブルに新規登録するようなビューである必要があります。
UserRegistration
では、クラス変数 form_class
を定義し、その form_class
に UserCreationForm
を指定しているため、レコードの新規登録を行うときに利用するフォームクラスは UserRegistration
となります。そして、この UserRegistration
は Django フレームワークの auth
アプリで定義されるモデルフォームクラスであり、そのベースとなるモデルクラスは User
となります(model
属性に User
が指定されている)。そのため、このモデルフォームクラスを利用したレコードの新規登録は User
のテーブルに対して行われることになり、上記を満たしていることになります。
この説明の中で登場した UserRegistration
については下記ページで、
モデルフォームクラスについては下記ページで解説していますので、詳細を知りたい方は別途これらのページを参照していただければと思います。
【Django入門8】モデルフォームの基本別途自身で カスタムユーザー
を定義するのであれば、settings.py
で AUTH_USER_MODEL
を定義し、その カスタムユーザー
を AUTH_USER_MODEL
に指定する必要があります。そして、この場合は UserRegistration
を継承するクラスを定義し、model
属性に カスタムユーザー
を指定する必要があります。このあたりに関しても上記で示した カスタムユーザー
の解説ページで説明していますので、必要に応じて カスタムユーザー
の解説ページを参照してください。
また、UserRegistration
では form_valid
を定義してオーバーライドを行なっており、CreateView
の form_valid
が行う処理に加え、login
関数によるログイン処理も実行するようになっています。そのため、ユーザーの新規登録に成功した際には、そのユーザーでのログインが自動的に実施されることになります。
UserList
UserList
は ListView
のサブクラスであり、ユーザーの一覧の表示を実現するビューとなります。より具体的には、UserList
は User
のインスタンスの一覧を表示します。
この ListView
に関しては下記ページで詳細を解説していますので、各クラス変数やメソッドの意味合いに関しては下記ページをご参照いただければと思います。
この UserList
は LoginRequiredMixin
を継承している点がポイントで、この 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_name
と LoginRequiredMixin
を継承するクラスのクラス変数 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
を下記のように変更してください。
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
を新規作成し、中身を下記のように変更してください。
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
を新規作成し、中身を下記のように変更してください。
<!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
を新規作成し、中身を下記のように変更してください。
<!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
を新規作成し、中身を下記のように変更してください。
<!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
のリクエストを送信する必要があるという点に注意してください。
リダイレクト先の設定
最後に、settings.py
を変更してログイン後のリダイレクト先の URL とログアウト後のリダイレクト先の URL の指定を行なっていきます。
前者に関しては settings.py
で LOGIN_REDIRECT_URL
を定義することで、後者に関しては settings.py
で LOGOUT_REDIRECT_URL
を定義することで、それぞれを指定することが可能です。
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.py
で UserList
にマッピングされている URL になります。なので、本来であれば UserList
によってユーザーの一覧ページが表示されるはずですが、現状は非ログイン状態であるため強制的にリダイレクトが行われることになります。
そして、このリダイレクトは UserList
が継承している LoginRequiredMixin
によって実施され、リダイレクト先は settings.py
で LOGIN_URL
に指定される URL となります。ですが、今回は settings.py
で LOGIN_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.py
で LOGOUT_REDIRECT_URL
に /accounts/login/
を指定しているため、ログインページが表示されることになります。
今回はログインは行わず、再度下記 URL をウェブブラウザで開いてみてください。
http://localhost:8000/accounts/list/
すると、最初に上記 URL をウェブブラウザで表示した時と同様にログインページにリダイレクトされるはずです。これは、先ほどの ログアウト
ボタンのクリックによってログアウトが実行され、非ログイン状態に戻ってしまったからになります。前述の通り、このリダイレクトは LoginRequiredMixin
によって実施されます。
ログイン(クエリパラメーター redirect
有りの場合)
ここまで説明した手順を実施してくださった方であれば、現在ログインページが表示されているはずです。
ここで注目していただきたいのがリダイレクトされた先の URL となります。ということで、ウェブブラウザのアドレスバーを確認してみてください。アドレスバーに表示される URL は下記のようなものになっており、クエリパラメーター redirect
が指定されていることが確認できると思います。
このようにリダイレクト先の URL にクエリパラメーター redirect
が追加されるようになっているのは UserList
が LoginRequiredMixin
を継承しており、さらにクラス変数 redirect_field_name
に 'redirect'
を指定しているからになります。そして、クエリパラメーターの redirect
の値は /accounts/list/
であり、これは非ログイン状態でアクセスしようとしたページの URL のルートパスになります。
また、表示されているログインページのソースを確認してみれば分かると思いますが、フォーム要素部分の HTML は下記のようになっており、name="redirect"
および value="/accounts/list/"
が指定された input
タグが存在することになります。そのため、送信
ボタンがクリックされた際には "redirect" : "/accounts/list/"
という redirect
フィールドを含むデータが送信されることになります。
<form action="/accounts/login/" method="post">
<input type="hidden" name="csrfmiddlewaretoken" value="略">
〜略〜
<input type="hidden" name="redirect" value="/accounts/list/">
〜略〜
ということで、次は実際にログインを行なってみましょう!
先ほどユーザーを新規登録したはずですので、その登録した username
と password
をフォームの各フィールドに入力して 送信
ボタンをクリックしてください。
username
と password
が正しければ、ログインが行われた後に自動的にユーザー一覧の表示ページに遷移するはずです。
このページは非ログインユーザーからはアクセス不可であるため、このページが表示されたことでログインが正常に実施されたことが確認できたことになります。また、このページに遷移したのは next フィールドを利用したリダイレクト で説明した ログイン前にアクセスしようとした URL
へのリダイレクトが上手く実現できているからで、具体的には「クエリパラメーター redirect
で指定された /accounts/list/
」を値とする redirect
フィールドがログインフォームから送信されたために /accounts/list/
へのリダイレクトが行われたことになります。
ログイン(クエリパラメーター redirect
無しの場合)
次は、クエリパラメーターが存在しない場合のログイン後のリダイレクトについて確認してみましょう!この場合は、settings.py
の LOGIN_REDIRECT_URL
で指定された URL へのリダイレクトが行われることになります。
再度、ユーザー一覧ページに設置された ログアウト
ボタンをクリックしてログインを実施してください。今回も、ログアウト後には自動的に下記 URL へリダイレクトされることになります。
http://localhost:8000/accounts/login/
そして、この場合は LoginRequiredMixin
によるリダイレクトが行われたわけではないので、リダイレクト先の URL にはクエリパラメーターが存在しないことになります。このように、直接ログインページを開かれた際にはクエリパラメーターは付加されません。また、クエリパラメーターが存在しないことは、ウェブブラウザのアドレスバーに表示される URL からも確認できると思います。
続いて、先ほどと同様に username
と password
を入力して 送信
ボタンをクリックしてください。今度はログイン後に下記 URL にリダイレクトされてユーザーの新規登録ページに自動的に遷移するはずです。先ほどログインを実施したときとは異なるページへのリダイレクトが行われている点がポイントになります。
http://localhost:8000/accounts/register/
そして、この違いはクエリパラメーター redirect
の有無によるものとなります。前述の通り、URL にクエリパラメーター redirect
が存在する場合は redirect
で指定される URL にリダイレクトされますが、存在しない場合は settings.py
の LOGIN_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 のサブクラス
であり、このクラスを継承するビューを定義することで、ウェブアプリにログイン機能を搭載することが可能となります。
LoginView
は FormView
のサブクラスであり、既にログインに特化する形で作り込まれた View のサブクラス
となります。したがって、他の View のサブクラス
に比べるとカスタマイズ要素は少なめです。
そのため、ログインは LoginView
を利用することで簡単にログインを実現することが可能です。LoginView
のサブクラスの定義を行うだけでほぼ出来上がりです。ただし、他のビューにアクセス制限を設ける必要があったり、リダイレクトに関してはややこしい点もあったりしますので、この辺りがウェブアプリにログイン機能を搭載する際のポイントになると思います。
様々なウェブアプリでログイン機能が搭載されていることからも分かるように、ログイン機能が実現できるようになれば、開発可能なウェブアプリの幅が一気に広がります。その分ウェブアプリ開発も楽しくなると思いますので、是非 LoginView
を利用したビューの作り方については覚えておいてください!
また、このサイトでは他の View のサブクラス
についても説明していますので、他のページも是非読んでみてください!