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

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

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

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

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

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

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

そして、名前の通り LoginView はログインを実現するための View のサブクラス となります。関数ベースビューでの解説にはなりますが、ログイン機能を持つウェブアプリに必要となる動作や処理については下記ページでまとめていますので、事前にこちらの前半だけでも読んでおくことをオススメします。

Djangoでのログイン機能の実現方法解説ページアイキャッチ【Django】ログイン機能の実現方法(関数ベースビュー編)

このページで目指すことは、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 アプリでは、下記のように認証やログイン等を実現するためのクラスや関数等が多数定義されています。

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

そして、今回扱う LoginView は、ここで紹介したようなクラスや関数を利用してログインを実現するビューとなります。ここで紹介したクラスや関数は以降の解説でも出てきますので、頭の片隅ででも覚えておいていただければと思います。

スポンサーリンク

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

MEMO

認証について補足しておくと、認証処理においては、フォームから送信されてきた username フィールドの値および password フィールドの値と同じフィールドの値を持つレコードがデータベースに存在するかどうかの確認が行われます

存在すれば認証 OK となります

デフォルトでは、そのようなレコードが存在するかどうかの確認は User という auth アプリで定義されるモデルクラスに対応するテーブルに対して行われます

そのため、認証で OK となるためには事前に User のテーブルにレコードを保存しておく必要があります

この辺りは、一般的なウェブアプリで事前にユーザー登録をしておかないとログインができないのと一緒です

クラスベースビューの場合、この User のテーブルへのレコードの保存は CreateView によって実現可能です

実際にユーザーを登録する例は LoginView の利用例 で示します

ログイン後のリダイレクト

この LoginView におけるポイントの1つはログイン後のリダイレクトになると思います。前述の通り、LoginView はログインに成功した後に特定の URL に対するリダイレクトレスポンスを返却します。そして、クライアント側はそのレスポンスを受け取り、指定された URL にリクエストを送信するようになります。

ログイン後に行われるリダイレクトの様子

LoginView では、このログイン後のリダイレクト先の URL は下記の3段階の判断が行われて自動的に設定されるようになっています。上側のものから順番に判断が行われ、当てはまる URL がリダイレクト先に設定されるようになっています。

  1. POST リクエストで送信されてくるデータに next フィールドが存在する時:
    • nextで指定される URL にリダイレクト
  2. クラス変数 next_page が定義されている時:
    • next_page で指定される URL にリダイレクト
  3. プロジェクトの settings.pyLOGIN_REDIRECT_URL が定義されている時;
    • LOGIN_REDIRECT_URL で指定される URL にリダイレクト

1. 〜 3. の全てに当てはまらない場合はリダイレクト先として /accounts/profile/ が設定されることになります(LOGIN_REDIRECT_URL のデフォルト値)。

ログイン前にアクセスしようとしたページへのリダイレクト

特にポイントになるのが 1. のリダイレクトです。このリダイレクトを利用することで、ユーザーをログイン後に “ログイン前にアクセスしようとしたページ” に自動的に遷移させることが可能となります。

まず、前述の通り、LoginView では POST リクエストによって認証とログイン処理が実施されることになります。POST リクエスト時に送信されてくるユーザー名(username フィールドの値)やパスワード(password フィールドの値)に基づいて認証及びログインが行われることになります。

さらに、LoginView では POST リクエストによって送信されてくるデータに next フィールドが存在し、その next フィールドの値が URL として妥当である場合、ログイン後に、その next フィールドで指定される URL をリダイレクト先とするリダイレクトレスポンスを返却するようになっています。

nextフィールドで指定されるURLへのリダイレクトレスポンスを返却する様子

その一方で、ログイン機能を搭載したウェブアプリにおいては、非ログイン状態ではアクセスを拒否するページを設けることが多いです。”見たいページにアクセスしようとしたけれど、ログインしていないため表示が拒否されてログインページにリダイレクトされてしまった” という経験をお持ちの方も多いのではないでしょうか?

非ログイン状態のユーザーのアクセスを拒否する様子

こういった “非ログインユーザーのアクセスの拒否” は Django でも可能で、この場合、非ログインユーザーがページにアクセスしようとするとログインページの URL へ強制的にリダイレクトされることになります。

もちろん、このリダイレクト先のログインページからログインを実施することは可能ですが、ユーザーが元々アクセスしたかったのはログインページの URL ではなく “ログイン前にアクセスしようとしていた URL” です。なので、ログイン後には “ログイン前にアクセスしようとしていた URL” にリダイレクトしてあげるのが親切ですよね。

ログイン後に"ログイン前にアクセスしようとしていたURL"にリダイレクトする様子

このようなログイン後の “ログイン前にアクセスしようとしていた URL” へのリダイレクトを実現するために、先ほど説明した next フィールドが利用されることになります。

前述の通り、POST リクエスト時に LoginViewnext フィールドを含むデータを受け取れば、自動的に LoginView がログイン後に next フィールドで指定される URL へのリダイレクトレスポンスを返却してくれることになります。

したがって、next フィールドに “ログイン前にアクセスしようとしていた URL” がセットされたデータを POST リクエスト時に LoginView へ送信されるようにしてやれば、上記のようなリダイレクトが可能ということになります。もちろん、ログインを行うためには username フィールドと password フィールドも一緒に送信されるようにしておく必要があります。

ログイン前にアクセスしようとしたURLへのリダイレクトを実現するためにPOSTリクエストで送信が必要なデータ

そして、ログインを行うための POST リクエストの送信はログインページから行われることになります。なので、ログインを実施する際に “ログイン前にアクセスしようとしていた URL” が値にセットされた next フィールドを含んだデータを送信するようにログインページを作ってやれば、上記のようなリダイレクトが可能となります。ページを構成するのは HTML であり、その HTML はテンプレートファイルから生成されることになるため、つまりはテンプレートファイルを上記のように作成する必要があります。

この辺りのテンプレートファイルの作り方の詳細に関しては、次の LoginView でのクラスベースビューの作り方 で解説していきます。まずは、”ログイン前にアクセスしようとした URL” へのリダイレクトが可能であることと、そのために POST リクエスト時に next フィールドを含むデータが送信されるようにすることが必要であることを覚えておいていただければと思います。

また、上記のようなリダイレクトを実現する上では非ログインユーザーからのアクセスを拒否する設定もビューに対して行う必要があります。これについても次の LoginView でのクラスベースビューの作り方 で解説していきます。

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

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

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

つまり、views.py に LoginView を継承するクラス(サブクラス)を定義し、views.py に定義したクラスにクラス変数やメソッドを定義することで 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でのログイン機能の実現方法解説ページアイキャッチ【Django】ログイン機能の実現方法(関数ベースビュー編)

LoginRequiredMixin によるリダイレクト

上記のように非ログインユーザーからのアクセスを拒否した場合、非ログイン状態でそのページにアクセスした場合には特定の URL に強制的にリダイレクトされることになります。この強制的なリダイレクトによって、非ログインユーザーからのアクセスを禁止する仕組みとなっています。

LoginRequiredMixinによって非ログイン状態のユーザーがログインページにリダイレクトされる様子

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

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

LOGIN_URLsettings.py で設定するパラメーターで、LOGIN_URL を指定しなかった場合はデフォルト設定の /accounts/login/ が指定されることになります。この LOGIN_URL にはログインページの URL を設定しておくことが一般的です。そして、ログインページは LoginView によって実現されるため、このリダイレクトによってクライアントから LoginView に対してリクエストが送信されることになります。

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

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

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

また、上記における next 以降に関しては ログイン前にアクセスしようとしたページへのリダイレクト で説明したようなリダイレクトを実現するためのクエリパラメーターとなります。

非ログイン状態のユーザーから LoginRequiredMixin を継承するビューに紐づけられた URL へのアクセスが発生した際、LoginRequiredMixin によってログインページへリダイレクトされ、その際に上記のように URL にクエリパラメーター next が付加されることになります。そして、この next には “ログイン前にアクセスしようとしていた URL” が指定されることになります。上記の場合は /forum/comments/ にアクセスしようとしてログインページにリダイレクトされるため、next には /forum/comments/ が指定されることになります。

このクエリパラメーター next を利用することで、ログイン後の “ログイン前にアクセスしようとしていた URL” へのリダイレクトが実現可能となります。この点については次の節で説明していきます。

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

また、LoginView では POST リクエストを受け取ってログインを行なった後にリダイレクトレスポンスの返却が行われることになります。このレスポンスにおけるリダイレクト先の設定に関しても LoginView のサブクラスを利用する上で重要なポイントとなります。

POST リクエストを受け取った際にリダイレクトレスポンスを返却する点に関して言えば CreateViewUpdateView 等も同様です。これらのビューの場合は、リダイレクト先の URL はクラス変数 success_url の定義や get_success_url のオーバーライドによって設定可能でした。

それに対し、LoginView の場合はクラス変数やメソッドのオーバーライドだけでなく、ログイン後のリダイレクト で説明したように、ログイン前にアクセスしようとしていた URL や settings.py での LOGIN_REDIRECT_URL の定義によってリダイレクト先の URL が設定されることになります。

特に LoginView の特徴になるのが、ログイン前にアクセスしようとしていた URL へのリダイレクトになります。この概要に関しては ログイン前にアクセスしようとしたページへのリダイレクト で解説しましたが、ここではこのリダイレクトの具体的な実現方法についてここで解説しておきたいと思います。

next へのリダイレクト

ログイン前にアクセスしようとしたページへのリダイレクト で説明したように、LoginViewPOST リクエスト時に受け取ったデータに next フィールドが存在する場合、その next フィールドに指定された URL をリダイレクト先とするリダイレクトレスポンスをログイン実施後に返却するようになっています(next フィールドの値が空文字列の場合は next フィールドは無視されることになります)。

nextフィールドで指定されるURLへのリダイレクトレスポンスを返却する様子

そのため、この next フィールドに “ログイン前にアクセスしようとしていた URL” がセットされていれば、ログイン後に “ログイン前にアクセスしようとしていた URL” へのリダイレクトレスポンスを返却することができることになります。これを受け取ったクライアントはログイン状態で “ログイン前にアクセスしようとしていた URL” にリダイレクトされることになり、元々表示したかったページ等の閲覧が可能となります。

そして、前述の通り、ログイン前のアクセスが許可されないページにアクセスした際には LoginRequiredMixin によってログインページ(LoginView)にリダイレクトされ、その際には URL のクエリパラメーター next に “ログイン前にアクセスしようとしていた URL” がセットされることになります。

この時、LoginViewGET リクエストを受け取ることになり、クエリパラメーター next も受け取ることになります。

GETリクエスト時にクエリパラメーターnextで指定されるURLがLoginViewに渡される様子

ただ、”ログイン前にアクセスしようとしていた URL” へのリダイレクトを実現するためには、この next にセットされている  “ログイン前にアクセスしようとしていた URL” を GET リクエスト時だけでなく POST リクエスト時にも LoginView が受け取れるようにする必要があります。

前述の通り、値が “ログイン前にアクセスしようとしていた URL” である next フィールドを含むデータを POST リクエスト時に LoginView が受け取れるようにさえしてやれば、後はその URL へのリダイレクトレスポンスを LoginView が自動的にクライアントに返却するようになります(POST リクエストが送信される目的はログインを行うことですので、そのための username フィールドや password フィールドも必要になります)。

nextフィールドに"ログイン前にアクセスしようとしていたURL"を持つデータをPOSTリクエスト時に送信できるようにする必要があることを示す図

では、値が “ログイン前にアクセスしようとしていた URL” である next フィールドを含むデータを POST リクエスト時に LoginView が受け取れるようするにはどうすれば良いでしょうか?

これに関しても ログイン前にアクセスしようとしたページへのリダイレクト で少し説明しましたが、ログイン時に POST リクエストを送信するのはログインページであるため、ログインページから username フィールドと password フィールドに加え、値が “ログイン前にアクセスしようとしていた URL” である next フィールドを含むデータを送信するようにしてやれば良いことになります。そして、ログインページは HTML によって表示され、さらにこの HTML は Django ではテンプレートファイルから生成されます。そのため、フォームから next フィールドを含むデータが送信されるようにテンプレートファイルを作成してやれば良いことになります。

ログイン用のフォームからnextフィールドを含むデータが送信されるようにする様子

これにより、GET リクエスト時に生成された HTML をレスポンスとして受け取ったクライアントがフォームからログインに必要な username フィールドと password フィールドを送信してログインしようとした際に、これらのデータに加え、値が “ログイン前にアクセスしようとしていた URL” である next フィールドが送信されるようになります。

具体的には、このようなテンプレートファイルは下記のように、<form></form>name="next"value="{{ next }}" の属性を指定した <input> タグを追加することで実現することができます。ちなみに type="hidden" を指定するのは、このフィールドを非表示にするためです。このフィールドはユーザーから入力してもらうためのものではないため非表示にしておきます。

nextの送信
<form action="{% url 'login' %}" method="post">
    {% csrf_token %}
    <input type="hidden" name="next" value="{{ next }}">
    <table>{{ form.as_table }}</table>
    <p><input type="submit" value="送信"></p>
</form>

上記では 'login'LoginView のサブクラスに紐付けされた URL であることを前提としています。さらに form はフォームクラスのインスタンスであることを前提としています。前述の通り、デフォルトでは formAuthenticationForm のインスタンスとなります。

詳細に関しては LoginView が生成するコンテキスト で説明しますが、LoginView が生成するコンテキストには form フィールドと next フィールドが存在します。そして、コンテキストの next フィールドの値はクエリパラメーター next で指定される値となります。したがって、LoginRequiredMixin によるリダイレクトによって LoginViewGET リクエストを受け取った際には、コンテキストの next フィールドの値は “ログイン前にアクセスしようとしていた URL” となります。

そのため、上記のように name="next" と value="{{ next }}" の属性を指定した  input タグを <form></form> の間に追加してやれば、送信 ボタンクリック時に AuthenticationForm の持つ username フィールドと password フィールドの値だけでなく、値が “ログイン前にアクセスしようとしていた URL” である next フィールドも送信されるようになります。

フォームからusernameとpasswordを送信する際に一緒にnextも送信される様子

そして、フォームからのデータ送信時には POST リクエストが送信されますので、LoginViewPOST リクエスト時に next フィールドを含むデータを受け取ることができるようになり、これによって “ログイン前にアクセスしようとしていた URL” へのリダイレクトを実現できることになります。

長々と説明してきたので “ログイン前にアクセスしようとしていた URL” へのリダイレクトは複雑なようにも思えたかもしれませんが、基本的には実現に必要となる実装はテンプレートファイルへの上記のような input のタグの追加のみで、あとは LoginView を継承するビューや LoginRequiredMixin を継承するビューを用意しておけば、これらのビューが適切にパラメーター next を扱って “ログイン前にアクセスしようとしていた URL” が自然と実現されることになります。

next フィールドが存在しない場合のリダイレクト

また、”ログイン前にアクセスしようとしていた URL” へのリダイレクトが不要であるのであれば、テンプレートファイルへの上記のような input のタグの追加を行わなければ良いです。これにより、フォームから送信されるデータに next フィールドが含まれなくなるため、”ログイン前にアクセスしようとしていた URL” へのリダイレクトが行われなくなります。

そして、フォームから送信されるデータに next フィールドが存在しない場合は、ログイン後のリダイレクト でも説明したように、”LoginView のサブクラスで定義したクラス変数 next_page” or “settings.py で定義した LOGIN_REDIRECT_URL” にリダイレクトされることになります。

送信されてきたデータにnextフィールドが孫zないしない場合のリダイレクトの例

これらのどちらか一方を定義しておけば十分ですが、もし両方を定義していた場合は next_page の方が優先されることになります。また、POST リクエスト時に送信されてくるデータに next フィールドが存在しない場合や next フィールドの値が空文字列の場合、上記のいずれかを定義していないと /accounts/profile/ へリダイレクトされることになります。

クエリパラメーター next が存在しない場合のリダイレクト

さらに、ログインページ表示時に URL にクエリパラメーター next が存在しない場合もあり得ます。前述の通り、非ログイン状態でアクセス不可なページを表示するビューを LoginRequiredMixin の継承によって定義しておけば、そのページに非ログイン状態でアクセスされた際にログインページへのリダイレクトが行われ、この際にクエリパラメーター next がリダイレクト先の URL に付加されることになります。

ですが、直接ログインページにアクセスされた際にはクエリパラメーター next が設定されません。そして、これは普通にあり得るユースケースとなります。

直接ログインページの表示のリクエストが送信された際にクエリパラメーターnextが指定されない様子

この場合、クエリパラメーター next が存在しないわけですから next へのリダイレクト で紹介したようなテンプレートファイルを用意したとしても、POST リクエスト時に送信されるデータにおける next フィールドは空文字列となります。そのため、next フィールドで指定される URL のリダイレクトは不可となります。

そして、この場合も先ほどと同様に、”LoginView のサブクラスで定義したクラス変数 next_page” or “settings.py で定義した LOGIN_REDIRECT_URL” にリダイレクトされることになります。

このように、クエリパラメーター next が指定されない場合もケアしてリダイレクト先を設定しておく必要がある点に注意が必要です。 

以上が、LoginView を利用してビューを作成する場合のポイントとなります。

他の View のサブクラス と同様の作り方にはなるのですが、LoginView に関しては既にログイン用途のビューとして作り込まれているためカスタマイズ要素は少ないです。

ですが、ログイン機能を有するウェブアプリに開発していく際には、非ログインユーザーからのアクセスを拒否するために他のビューで LoginRequiredMixin を継承させる必要がある場面も多いと思います。また、ログイン機能を搭載した際にはリダイレクトが様々な場面(非ログインユーザーがアクセス不可なページにアクセスした時、ログインに成功した時など)で行われますので、このリダイレクトについても理解しておくと良いと思います。

スポンサーリンク

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 のサブクラスに利用させるようにしたいのであれば、次に説明する authentication_form を定義してやれば良いです。

スポンサーリンク

authentication_form

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

したがって、LoginView のサブクラスで form_classauthentication_form の両方が定義されていない場合は AuthenticationForm が自動的に LoginView のサブクラスが利用するフォームクラスとして設定されることになりますが、authentication_form を定義しておけば LoginView のサブクラスの利用するフォームクラスとして AuthenticationForm 以外のものを設定することが可能となります(別に form_class を定義して AuthenticationForm から変更するのでも良いです)。

ただし、クラス変数 authentication_form を定義する場合、基本的には authentication_form には “AuthenticationForm のサブクラス” を指定する必要があるという点に注意してください。単なるフォームクラス(forms.Form のサブクラス)を authentication_form に指定してもウェブアプリ動作時に例外が発生することになります。

next_page

next_pageログイン後のリダイレクト先の設定 で説明したようにログイン後のリダイレクト先を指定するためのクラス変数になります。next_page のデフォルトは None で、next_pageNone の場合は settings.py で定義した LOGIN_REDIRECT_URL で指定した URL がログイン後のリダイレクト先の URL に設定されることになります。また、”ログインフォームから送信されてきたデータに next フィールドが存在する” & “next フィールドが空文字でない” 場合は next_page よりも next で指定された URL がリダイレクト先の URL として優先して設定されることになります。

さらに、next_page には URL そのものではなく URL の名前などを指定することも可能です。

redirect_field_name

先ほども少し説明しましたが、”ログインフォームから送信されてきたデータに next フィールドが存在する” & “next フィールドが空文字でない” 場合、LoginView のサブクラスはログイン後に next フィールドの値となる URL に対するリダイレクトレスポンスを返却します。

nextフィールドで指定されるURLへのリダイレクトレスポンスを返却する様子

このリダイレクト先となる URL を指定するフィールドのフィールド名 next は、ここで説明するクラス変数 redirect_field_name の定義により変更することが可能です。

例えば下記のようにクラス変数 redirect_field_name を定義した場合、ログインフォームから送信されてきたデータの redirect フィールドが空文字でない場合に UserLoginView はログイン後に redirect フィールドの値となる URL に対するリダイレクトレスポンスを返却するようになります。

redirect_field_nameの定義
class UserLoginView(LoginView):
    template_name = 'accounts/login.html'
    redirect_field_name = 'redirect'

ただし、このようにクラス変数 redirect_field_name を定義しただけでは next へのリダイレクト で説明したような処理の流れが実現できないので注意してください。

クラス変数 redirect_field_name を定義した場合、redirect_field_name の指定値に応じて LoginView のサブクラスが GET リクエスト時に取得しようとするクエリパラメーターの変数名も変化します。例えば上記のように UserLoginView を定義した場合、この UserLoginView はクエリパラメーター redirect からコンテキストにセットする URL を取得するようになります。また、コンテキストのキー名も 'redirect' に変化することになります。

redirect_field_nameの整合性の説明図2

それに対し、クエリパラメーターをセットするのは LoginRequiredMixin の役目であり、前述の通り、デフォルトでは LoginRequiredMixin はリダイレクト先とする URL をクエリパラメーターの next にセットするようになっています。

redirect_field_nameの整合性の説明図2

つまり、LoginView のサブクラスで redirect_field_name を変更してしまうと、LoginRequiredMixin の動作と話が合わなくなってしまうことになります。そのため、LoginView のサブクラスで redirect_field_name を変更する場合、LoginRequiredMixin を継承するクラス側の設定も変更が必要となります。

具体的には、LoginRequiredMixin でもクラス変数 redirect_field_name が定義されているため、LoginRequiredMixin を継承する側のビューでもクラス変数 redirect_field_name を定義して指定値を変更する必要があります。そして、そのクラス変数 redirect_field_name への指定値は LoginView のサブクラス側と合わせる必要があります。

また、コンテキストにセットされているデータを参照するのはテンプレートファイルですので、next ではなく redirect_field_name に指定されている名前の変数を参照するようにする必要があります。さらに、フォームから送信されるデータに redirect_field_name の名前のフィールドが含まれるようにするため、フォームから送信されるデータの名称が redirect_field_name の指定値となるよう、追加する input タグの name 属性を変更する必要もあります。

下記は、redirect_field_name = 'redirect' とした場合のテンプレートファイルにおける form タグ部分の例となります。

required_field_name変更時のテンプレート
<form action="{% url 'login' %}" method="post">
    {% csrf_token %}
    <input type="hidden" name="redirect" value="{{ redirect }}">
    <table>{{ form.as_table }}</table>
    <p><input type="submit" value="送信"></p>
</form>

こんな感じで、redirect_field_name を変更すると様々な実装箇所に影響を及ぼすことになるので注意してください。また、よっぽどのことがない限り変更する必要もないと思います。一応 LoginView の利用例 でクラス変数 redirect_field_name の変更例を紹介します。

スポンサーリンク

redirect_authenticated_user

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

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

それに対し、 redirect_authenticated_userTrue の場合は他のページへのリダイレクトが行われるようになります。つまり、ログイン状態のユーザーはログインページを表示できなくなります。既にログイン済みのユーザーにログインページを表示する必要もないので、他のページにリダイレクトするというわけです。

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

そして、このリダイレクト先はログイン後のリダイレクトと同様の仕組みで決定されることになります。ただし、基本的には既にログイン済みのユーザーが直接ログインページにアクセスしてきた時に発生するリダイレクトとなるためクエリパラメーター next は設定されることがなく、クラス変数 next_page or settings.py で定義した LOGIN_REDIRECT_URL に指定されている URL にリダイレクトが行われることになるはずです。

redirect_authenticated_user のデフォルト値は False となりますので、ログイン済みのユーザーが再度ログインページにアクセスした際にリダイレクトするようにしたいのであればクラス変数 redirect_authenticated_user  を定義し、True を指定しておく必要があります。

template_name

template_name は、他の View のサブクラス 同様に LoginView のサブクラスが利用するテンプレートファイルを指定するクラス変数となります。デフォルトは 'registration/login.html' となります。

LoginView のメソッド

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

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

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

スポンサーリンク

LoginView のメソッド一覧

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

  • dispatch:リクエストや設定に応じた処理の振り分けを行う
  • 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_redirect_url メソッドや get_default_redirect_url メソッドが実行されることで決定されることになります。

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

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

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

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

これらの中でテンプレートファイルから参照する機会が多いのが 'form''next' になると思います。'form' には AuthenticationForm のインスタンスがセットされており、これをテンプレートファイルから {{ form.as_table }} 等で出力すれば、通常のフォームクラスと同様にフォームの表示を行うことができます。authentication_form で説明したように、クラス変数 authentication_form を定義することで AuthenticationForm 以外のフォームクラスをテンプレートファイルから参照することができるようになります。

また、'next' には “ログイン前にアクセスしようとしていた URL”  がセットされることになりますので、next へのリダイレクト で紹介したようなテンプレートファイルを用意しておけば、この URL をフォームから送信することができるようになります。そして、それによってログイン後に “非ログイン状態でアクセスしようとしていたページの URL” へのリダイレクトを実現することができるようになります。

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

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 のインスタンスをレコードとして保存するためのテーブルや、セッション情報のレコードを保存するためのテーブルが必要となります。

今回はモデルクラスをは定義していないため、下記の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 を継承するクラスであり、ユーザーログインを実現するビューとなります。

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

UserLogin ではクラス変数 redirect_field_name を定義して 'redirect' を指定しているため、GET リクエストの URL にクエリパラメーター redirect が含まれる場合は、その後にログインを実施した際にクエリパラメーター redirect で指定される URL へのリダイレクトレスポンスが返却されることになります。これは、ログイン前にアクセスしようとしたページへのリダイレクト で説明したリダイレクトになるのですが、redirect_field_name を定義しているため UserLogin がリダイレクト先として扱う URL の取得先がクエリパラメーターの next から redirect に変化している点に注意してください。

また、このようなリダイレクトレスポンスの返却を実現するためにはテンプレートファイル accounts/user_login.html 側での redirect の扱いも必要となり、これについては後述で解説します。

さらに、UserLogin ではクラス変数 redirect_authenticated_user を定義して True を指定しているため、ログイン中のユーザーからのアクセスによって UserLogin が動作する際には即座に特定の URL へのリダイレクトレスポンスが返却されることになります。そして、この “特定の URL” は UserLogin のクラス変数 next_page で指定される URL or settings.pyLOGIN_REDIRECT_URL で指定される URL のどちらか一方となります。今回の例ではクラス変数 next_page は定義しないため、後ほど settings.py を変更して LOGIN_REDIRECT_URL の定義を行い、LOGIN_REDIRECT_URL に指定した URL にリダイレクトされるようにしていきます。

UserLogout

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

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

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

この UserLogout に紐づけられた URL にアクセスすると、そのユーザーがログアウト状態になります。そして、その後に特定の URL へのリダイレクトレスポンスが返却されることになります。この “特定の URL” は、UserLogout のクラス変数 next_page で指定される URL or settings.pyLOGOUT_REDIRECT_URL で指定される URL のどちらか一方となります。今回の例ではクラス変数 next_page は定義しないため、後ほど settings.py を変更して LOGOUT_REDIRECT_URL の定義を行い、LOGOUT_REDIRECT_URL に指定した URL にリダイレクトされるようにしていきます。

UserRegistration

UserRegistrationCreateView を継承するクラスであり、ユーザーの新規登録を実現するビューとなります。より具体的には、UserRegistration はフォームから送信されてきたデータに基づいて User のインスタンスを生成し、それをレコードとしてデータベースに保存します。

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

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

ログイン関連の情報のみを補足しておくと、UserLogin が認証を実施する際にはユーザーがフォームに入力した情報と、この UserRegistration によって保存されているレコードの usernamepassword とが一致するかどうかの検証が行われることになります。

また、UserRegistration ではクラス変数 form_classUserCreationForm を指定しており、この UserCreationForm がフォームとして表示されると username(ユーザー名)と password(パスワード)、さらには確認用のパスワードの3つのフィールドが表示されます。

UserCreationFormによって表示されるフォーム

そして、このフォームからデータが送信されてきた際には、入力された usernamepassword がフィールドにセットされた User のインスタンスがレコードとしてデータベースに保存されることになります。さらに、この password は暗号化された状態(ハッシュ化された状態)で保存されることになります。

単なるモデルフォームクラスを定義し、それをクラス変数 form_class に指定してユーザーを作成するようにするのでも良いのですが、暗号化などのセキュリティを考慮すると、ログイン用のユーザーを新規登録するための CreateView のサブクラスでは、クラス変数 form_class には UserCreationForm を指定するのが良いです。もしくは UserCreationForm のサブクラスを指定するのでも良いです。

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

UserList

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

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

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

この UserListLoginRequiredMixin を継承している点がポイントで、この LoginRequiredMixin の継承により UserList に対応する URL へのアクセスは非ログインユーザーからは禁止されることになります。より具体的には、非ログインユーザーが UserList に対応する URL にアクセスしてきた際には強制的にログインページにリダイレクトされることになります(リダイレクトレスポンスが返却される)。

この “ログインページ” の URL は settings.py での LOGIN_URL の定義によって指定可能です。LOGIN_URL を定義しなかった場合は、LOGIN_URL にはデフォルトで /accounts/login/ が指定されるようになっています。今回は settings.py での LOGIN_URL を指定しないため /accounts/login/ にログインページが紐付けられるように urls.py での URL とビューとのマッピングを行う必要があります。

さらに、LoginRequiredMixin ではクラス変数 redirect_field_name'next' が定義されており、LoginRequiredMixin を継承するビューに対応する URL へのアクセスが行われた際には、クエリパラメーター next に “ログイン前にアクセスしようとしていた URL” として設定されることになります。

ただし、今回は UserList 側でクラス変数 redirect_field_name の値を 'redirect' で上書きしているため、クエリパラメーター redirect に “ログイン前にアクセスしようとしていた URL” が設定されることになります。そして、UserLogin でも クラス変数 redirect_field_name の値に 'redirect' を指定しているため、このクエリパラメーター redirect への指定値を受け取ってログイン後に redirect で指定される URL へのリダイレクトを行うことが可能となっています。

このように、クラス変数 redirect_field_name を定義する際には、その指定値が LoginView を継承するビューと LoginRequiredMixin を継承するビューとで一致させておく必要があります(後述で説明するテンプレートファイルに関しても参照する変数名等を一致させておく必要があります)。まぁ、クラス変数 redirect_field_name を定義しなかった場合の redirect_field_name のデフォルト値は両方のクラスで 'next' となっており、わざわざ変更しなければ redirect_field_name への指定値が不一致となることはありません(今回はあえて変更する例を示しています)。

ビューと 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 %}
            <input type="hidden" name="redirect" value="{{ redirect }}">
            <table>{{ form.as_table }}</table>
            <p><input type="submit" value="送信"></p>
        </form>
    </main>
</body>
</html>

基本的にはフォームを表示するだけのテンプレートファイルになります。ただし、next へのリダイレクト で説明したように、クエリパラメーターで指定される URL も含めてフォームから送信されるように form タグの中に input タグを追加しています。今回の例ではビューでのクラス変数の定義によって “ログイン前にアクセスしようとしていた URL” を指定するクエリパラメーターの変数名は 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>
    </main>
</body>
</html>

スポンサーリンク

リダイレクト先の設定

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

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

また、前者に関しては、ログインページを表示するための GET リクエスト時にクエリパラメーターで redirect が指定されていない場合のリダイレクト先の URL となります。これらが指定されている場合はクエリパラメーター redirect で指定される URL がログイン後のリダイレクト先の URL となります。

MEMO

もう少し細かく説明すると、クエリパラメーターで redirect が指定されていたとしても、POST リクエスト時に送信されてくるデータに redirect フィールドが存在しない場合はログイン後に LOGIN_REDIRECT_URL で指定される URL へのリダイレクトが行われることになります

ただし、今回はクエリパラメーターで redirect が指定された場合には必ず POST リクエスト時に送信されてくるデータにも redirect フィールドが追加されるようにテンプレートファイル等を作成しているため、クエリパラメーターで redirect が指定されれば redirect で指定される URL へのリダイレクトが行われるようになっています

今回は、下記を 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 のインスタンスがレコードとしてデータベースに保存されることになります。さらに、ユーザーの新規登録後に自動的に /accounts/list/ にリダイレクトされてユーザー一覧が表示されるはずです(まだユーザーは一人しかいませんが…)。

ユーザー一覧が表示される様子

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

ログアウト

続いて、ウェブブラウザで下記 URL を開いてください。

http://localhost:8000/accounts/logout/

この URL をウェブブラウザで開くとログアウトが行われます。そして、settings.pyLOGOUT_REDIRECT_URL/accounts/login/ を指定しているため、ログインページが表示されることになります。

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

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

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

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

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

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

リダイレクト先のURL

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

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

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

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

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

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

ユーザー一覧が表示される様子

このページはログイン中でないと表示できないため、このページが表示されたことでログインが正常に実施されたことが確認できたことになります。また、このページに遷移したのはフォームから送信されたデータにおける redirect フィールドが /accounts/list/ であるためになります。このように、クエリパラメーターで指定された URL のルートパスをフォームを介して LoginView のサブクラスに渡すことで、ログイン後にクエリパラメーターで指定された URL へのリダイレクトが可能となります。

作成したウェブアプリで実現されたリダイレクト

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

それに対し、クエリパラメーターで URL のルートパスが指定されなかった場合は settings.pyLOGIN_REDIRECT_URL で指定される URL へのリダイレクトが行われることになります。

これについても確認しておきましょう!

まず、下記 URL をウェブブラウザで開いてログアウトを実施してください。

http://localhost:8000/accounts/logout/

すると、ログアウト後に自動的に下記 URL へのリダイレクトが行われることになります。

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

この場合、非ログイン状態のユーザーでアクセス禁止されていたページへのアクセスを行なったわけではなく、直接ログインページにアクセスされたことになるため、ウェブブラウザの 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 へのリダイレクトが行われることになります。そして、このリダイレクト先は LoginView のサブクラスのクラス変数 next_page を定義している場合は next_page に対応する URL、定義していない場合は settings.py での LOGIN_REDIRECT_URL に指定される URL となります。

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

ここまで説明した内容から感じ取ってもらえたと思いますが、LoginView を使えば簡単にログイン機能をウェブアプリに搭載することが可能となります。ですが、色んな箇所でリダイレクトが行われるため、リダイレクトには関してはちょっとややこしいです。この辺りに関しては自身でウェブアプリを開発し、リダイレクトに関して色々試行して慣れていっていただければと思います。

まとめ

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

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

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

なので、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の使い方(クラスベースビューでのログアウトの実現)

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