【Django】ログイン機能の実現方法(関数ベースビュー編)

Djangoでのログイン機能の実現方法解説ページアイキャッチ

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

このページでは、Django での「ログイン機能」の実現方法について解説していきたいと思います!

皆さんも Twitter や Instagram を通じてご存知の通り、ウェブアプリにはログイン機能を持ち、ログインしたユーザーに応じた動作・表示を実現するものが多く存在します。

そういったアプリを開発していけるよう、このページでは Django で開発するウェブアプリに「ログイン機能」を持たせるために必要な実装について解説していきたいと思います。

正直いうと、ログイン機能を実現する際の View に関しては、Django に標準で用意されているクラスを継承しながらクラスベースで作成した方が簡単だと思います。ですが、今回はあえて、関数ベースの View でログイン機能を実現していきたいと思います。

関数を自作するので手間はかかりますが、その分 Django においてログインがどのように実現されているかを理解しやすくなると思います。

ログイン機能を実現するために必要なこと

まずは、ログイン機能を実現するために必要となる処理や動作の全体像をまとめておきたいと思います。

最初に箇条書きで列挙しておくと、ウェブアプリにログイン機能を持たせるためには、下記のような処理や動作を実現していく必要があります。

  • ユーザーアカウントの登録
  • ログイン
  • ログイン後のリダイレクト
  • 未ログインユーザーからのアクセス禁止
  • ログインユーザーに応じたページ表示
  • ログアウト

実際にログインを行うのは2つ目の項目の「ログイン」となりますが、これだけ実現してもウェブアプリにログイン機能を持たせる意味があまりないです。例えばログインしなくてもウェブアプリが利用できたりしてしまいます。

意味のある「ログイン」として機能させるためには、上記の6つくらいの処理や動作を実現した方が良いです。

ここからは、上記で挙げた処理について1つ1つ簡単に説明を行なっていきます。ちょっと実現が難しそうに感じることもあるかもしれませんが、これらは Django に用意されたクラスや関数を利用すれば簡単に実現することが出来ます!

ユーザーアカウントの登録

まず、ログイン機能を実現するためには、ユーザーアカウントの登録を行えるようにする必要があります。

ユーザーアカウントは管理画面から作成することも可能ですが、この場合は管理者権限を持つユーザーしかユーザーアカウント作成ができません。

今回は管理者権限を持つユーザーだけでなく、ウェブアプリの利用者自身がユーザーアカウントを作成できるように、下の図のようなユーザーアカウント登録フォームからユーザーアカウントを登録できるようにしていきたいと思います。

ユーザーアカウント登録画面を示す図

これを実現するためには、上の図のような登録フォームを表示できるようにすることと、フォームに入力された情報が送信されてきた際に、その情報をデータベースのユーザー管理テーブルに保存するような処理が必要になります。

ユーザーがアカウント登録を行う様子

そして、このデータベースへの保存により、ユーザーアカウントの登録が行われることになります。

で、Django の場合、データベースのテーブルは Model 毎に作成されることになります。つまり、ユーザー管理テーブルを作成するための Model が必要ということになります。

ただ、このページでは、この Model として Django に標準で用意されている User を利用していきたいと思います。

実は Django 公式では User をそのまま利用することは非推奨されています。ですが、ログイン機能の実現の流れを理解することだけ考えると、そのまま User を利用したが方が分かりやすと思います。そのため、今回は User をそのまま利用させていただきます。

実際にウェブアプリを公開する際などは自身でユーザーを管理する Model を用意する必要があります。この Model の作り方については下記ページで解説していますので、本格的なログインを実現したい際には是非参考にしてください。

【Django】カスタムユーザー(独自のユーザー)の作り方【AbstractUser編】 カスタムユーザーをAbstractBaseUserを継承して作成する手順の解説ページアイキャッチ 【Django】カスタムユーザー(独自のユーザー)の作り方【AbstractBaseUser編】

スポンサーリンク

ログイン

さらに、登録されたユーザーアカウントでログインを行えるようにしていく必要があります。

これは、例えば下の図のようにログインフォームを表示し、

ログインフォームを示す図

さらにフォームから送信されてきたユーザー名やパスワード等の情報から、登録されたユーザーアカウントであるかどうかの認証を行うような処理が必要になります。

要は、フォームから送信されてきたユーザー名がユーザ管理テーブルに保存されているかどうか、さらに送信されてきたパスワードが正しいのかどうかを確認する必要があります。

ユーザーがログインを行う様子

そして認証が OK の場合のみ、フォームを送信してきたユーザーがログインできるようにする必要があります。

ちょっと難しそうではありますが、認証・ログインともに Django に用意されているクラスや関数を利用することで簡単に実現することが出来ます。

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

また、多くのウェブアプリでは、ユーザーがログインできた際にはログインフォームが再び表示されるのではなく、例えばユーザーのホームのページのような他のページへのリダイレクト(自動的に転送)が行われるようになっています。

ログイン後にリダイレクトで他のページに遷移する様子

このリダイレクトはログインには必須ではないですが、ユーザーのアプリの使いやすさを考えると、こういったログイン後のリダイレクトも実現しておいた方が良いです。

未ログインユーザーのアクセス禁止

さて、このページではログイン機能を実現しようとしているのですが、そもそもユーザーにわざわざログインをさせる目的は何でしょうか?

ウェブアプリによって目的は異なると思いますが、下記のような目的が挙げられると思います(後者に関しては次の ログインユーザーに応じたページ表示 で解説します)。

  • ログインユーザーしかウェブアプリを使用できないようにする
  • ユーザーを識別してユーザーに応じた処理を行う(ユーザーに応じた情報を表示する)

前者を実現するためには、未ログインのユーザーが「ログイン中でないと見れないページ」にアクセスしてきた時にログインページにリダイレクトを行うような処理が必要になります(エラーを表示するなどの手段で実現することも可能です)。

未ログインユーザーがアクセスしようとするとログインページにリダイレクトされる様子

ちょっと面倒そうですが、これは login_required というデコレータを利用するだけで簡単に実現することが出来ます。

スポンサーリンク

ログインユーザーに応じたページ表示

また、先ほどログイン機能を設けることの目的の2つ目として挙げたように、ログイン機能を設け、ユーザーに応じて表示する情報を変化させるようなことも可能です。

例えばショッピングアプリで考えると、購入履歴のページは当然ログイン中のユーザーに応じて変化させる必要があります。こういったログインユーザーに応じたページ表示もログイン機能を持つウェブアプリに必要になる処理となります。

購入履歴のページに表示される内容が表示するユーザーによって変化する様子

こういったユーザーの情報は、ユーザーを管理する Model、もしくは、その Model と関連付けされている Model から取得することが出来ます。

今回はユーザーを管理する Model としては Django に標準で用意されている User を使用しますので、User に用意されている情報しかページに表示することが出来ません。しかし、ユーザーを管理する Model を自作して必要なフィールドを持たせるようにしてやれば、前述のような購入履歴などの様々な情報を表示することができるようになります。

スポンサーリンク

ログアウト

また、ログインしたユーザーがいつでもログアウトを行えるようにログアウト機能も備えておいた方がセキュリティ的に良いと思います。

ログアウト機能に関しても、Django に用意されている logout 関数の利用によって簡単に実現することが可能です。

ログイン機能を持つウェブアプリの作り方

大体ログイン機能を持つウェブアプリを実現するために必要な処理や動作については理解していただけたのではないかと思いますので、ここからは、具体的にソースコード等も参照しながらログイン機能を持つウェブアプリの作り方について解説していきたいと思います。

スポンサーリンク

事前準備(プロジェクトやアプリの作成など)

以降ではソースコードも変更しながら実際にログイン機能を実現していきたいと思いますので、まずは事前準備としてプロジェクトやアプリの作成・urls.py の設定等を行なってアプリの大枠だけ作成していきたいと思います。この辺りは通常のウェブアプリを開発するときと同様にして作成して問題ありません。

今回は、プロジェクト名は login_project・アプリ名は login_app として解説を行なっていきます。

プロジェクトの作成

まずは通常通り、プロジェクトを下記コマンドで作成していきましょう!

% django-admin startproject login_project

以降では、ファイルのパスは上記で作成した「login_project フォルダからの相対パス」で記述していきますので、その点にご注意してください。

また、django-admin 実行時にエラーが出る方は下記ページを参考にしていただければと思います。

django-adminが実行できない場合の対処法の解説ページアイキャッチ 【Python/Django】django-admin が実行できない場合の対処法(”コマンドが見つかりません”の対処法)

アプリの作成

先ほど実行したコマンドにより login_project フォルダが作成されますので、次は login_project フォルダの中で下記コマンドを実行してアプリを作成します。今回作成するアプリの名称は、前述の通り login_app とします。

% python manage.py startapp login_app

アプリの登録

続いて、login_project/settings.py に下記の1行を追記してプロジェクトにアプリの登録を行います。

アプリの登録
# Application definition

INSTALLED_APPS = [
    'login_app', # 追記
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

View の大枠の作成

次は、login_app/views.py を下記のように変更して View の大枠を作成しておきます。

現状では各関数は何もしない状態になっていますが、以降で views.py を作り込んでいくことでログイン機能を実現していきます。

Viewの大枠
from django.shortcuts import render

def signup_view(request):
    pass

def login_view(request):
    pass

def logout_view(request):
    pass

def user_view(request):
    pass

def other_view(request):
    pass

上記の login_app/views.py の各関数は、次のような役割を持つ関数となるように今後の解説の中で実装を行なっていきます。

  • signup_view:ユーザーアカウントの登録
  • login_view:ユーザーのログイン
  • logout_view:ユーザーのログアウト
  • user_view:ログインユーザーの情報の表示
  • other_view:他のユーザーの情報の表示

View と URL の関連付け

さらに、先ほど作成した View の関数と URL の関連付けを login_project/urls.pylogin_app/urls.py とで行なっていきます。

まずは、login_project/urls.py を下記のように変更し、login_app/ から始まる URL が指定された際に login_app/urls.py を参照するようにします。

プロジェクトのurls.py
from django.contrib import admin
from django.urls import path, include

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

さらに、login_app/urls.py を下記のように新規作成します。

アプリのurls.py
from . import views
from django.urls import path

urlpatterns = [
    path('signup/', views.signup_view, name='signup'),
    path('login/', views.login_view, name='login'),
    path('logout/', views.logout_view, name='logout'),
    path('user/', views.user_view, name='user'),
    path('other/', views.other_view, name='other'),
]

これにより、例えば http://localhost:8000/login_app/signup/ の URL にアクセスがあった際に(URL へのリクエストがあった際に)、login_app/views.pysignup_view 関数が実行されるといった具合に、各 URL と View の関数が関連付けられることになります。

マイグレーション

最後に、startproject 実行時に作成された login_project フォルダの中で下記のコマンドを実行してマイグレーションを行います(models.py を変更していないため、今回は makemigrations は不要です)。

% python manage.py migrate

models.py は変更していませんが、Django では認証用のモデルとして User がデフォルトで利用されるようになっているため、models.py にモデルを定義しなくても上記コマンドを実行すればデータベースに User 用のテーブル(auth_user)が作成される事になります。

Userモデル用のテーブル

ユーザーアカウントを登録した場合には、この auth_user にユーザーの情報をレコードとして保存することになりますし、ログインを行う際には、ログインフォームに入力されたユーザー名のレコードが auth_user に保存されているか、さらに入力されたパスワードがそのレコードのパスワードと同じであるかどうかの確認が行われることになります。

ちなみに、上の図は VSCode のプラグインである SQLite Viewer を利用してテーブルの中身を表示した結果となります(まだ中身は空ですが…)。

例えばユーザーアカウントを登録した際には上の図のテーブルに登録されたユーザーの情報がレコード(行)として追加されることになります。

実際のアプリを動作させている際に、ユーザーアカウントがうまく登録されているかどうかを確認したりする際に便利なので、こういったテーブルの中身をお手軽に確認できる環境があるとウェブアプリの開発が捗ると思います。

このプラグインについては下記ページでも紹介していますので、使い方等の詳細は下記ページを参考にしていただければと思います。

VSCodeでデータベースのテーブルの中身を表示する方法の解説ページアイキャッチ 【Django/VSCode】データベースのテーブルの中身を表示する

以上で、ログイン機能を持つウェブアプリを開発していく上での大枠を作成できたことになります。ここからは、ログイン機能を実現するために必要なこと で紹介した各項目を1つ1つの実現の仕方の説明・具体的な実装例の紹介をしていきたいと思います。

ユーザーアカウントの登録

では、まずはユーザーアカウントの登録を実現していきたいと思います。

ユーザーアカウントの登録は、ユーザーの情報(ユーザー名やパスワードなど)をデータベースにレコードとして保存することで実現することが出来ます。

より具体的には、まず登録するユーザーの情報の入力フォームを表示し、その入力フォームからユーザーの情報が送信されてきた際に、そのユーザーの情報をデータベースにレコードとして保存してやることでユーザーアカウントの登録を実現することが出来ます。

ユーザーアカウント登録用の Model の作成

上記のレコードの保存において、レコードの保存先のテーブルは「ユーザーの情報を管理する Model」から生成されるテーブルとなります。

そのため、ユーザーアカウント登録や後述で説明するログインを行うためには、あらかじめ「ユーザーの情報を管理する Model」を定義しておく必要があります。

ただ、前述の通り、今回は「ユーザーの情報を管理する Model」として Django に標準で用意されている User をそのまま利用しますので、今回は Model の定義は不要となります(models.py は変更不要)。

ユーザーアカウント登録用の Form の作成

また、一般的なウェブアプリやウェブサイトにおいては、ユーザーアカウント登録時にはユーザー名等のユーザーを識別するためのユニークな情報(他の人と重複しない情報)とパスワードの入力が必要な場合が多いです。

さらに、確認用としてパスワードの入力を2回要求されるのが一般的だと思います。

このような入力フォームは、Django に用意されている UserCreationForm を継承して Form を定義することで簡単に実現することが出来ます。

例えば、下記のように login_app/forms.py を新規作成してやれば、ユーザアカウント登録用の入力フォーム SignupForm を定義することが出来ます。

アカウント登録用フォームの定義
from django import forms
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm

class SignupForm(UserCreationForm):
    class Meta:
        model = User
        fields = ['username', 'email', 'password1', 'password2']

UserCreationFormforms.ModelForm のサブクラスであり、基本的には forms.ModelForm のサブクラスとして定義したフォームと同様の使い方をすることが出来ます。

forms.ModelForm では model に指定したモデルの持つフィールドに基づいて入力フォームを作成することができます。上記では model = User を指定しているため、User モデルの持つフィールドに合わせて入力フォームを作成することができることになります。

ただ、User モデルにはユーザー名やパスワードだけでなく、「最終ログイン日時」や「アカウント作成日時」、「ユーザの持つ権限」などを設定するフィールドを持っており、全てをフォームとして表示してしまうとユーザーの入力が大変になってしまいます。

そのため、fields を指定して表示するフィールドを下記の4つに絞るようにしています。

  • 'username':ユーザー名
  • 'email':メールアドレス
  • 'password1':パスワード
  • 'password2':パスワード(確認用)

'password1''password2' に関しては User モデルのフィールドに存在しないのですが、UserCreationForm のサブクラスとしてフォームを定義することで入力フォームとして表示することができるようになります(fields に指定することができるようになる)。

ユーザーアカウント登録用の Template の作成

次に、ユーザーアカウント登録用のフォームをページ上に表示するための Template を login_app/templates/login_app/signup.html として作成していきたいと思います(新規作成が必要なファイルになります)。

ユーザーアカウント登録用のフォームといっても、通常のフォームと同様にして Template に組み込むことが出来ます。

例えば、View から先ほど定義した SignupForm のインスタンスを form として受け取るのであれば、{{ form.as_p }} と Template に記述しておけば、下記の4つの入力フィールドを持つフォームが段落形式でページに表示されることになります。

  • 'username':ユーザー名
  • 'email':メールアドレス
  • 'password1':パスワード
  • 'password2':パスワード(確認用)

もちろん CSS 等を利用すればユーザーアカウント登録用のフォームの見た目を良くすることもできますが、今回はログインの機能に焦点を当てて解説していきたいため、下記のようにシンプルな login_app/templates/login_app/signup.html を新規作成していきたいと思います。

アカウント登録用フォームの表示
{% load static %}
<!doctype html>
<html lang="ja">
<head>
    <meta charset="utf-8">
    <title>ユーザー登録</title>
</head>
<body>
    <h1>ユーザー登録</h1>
    <form action="{% url 'signup' %}" method="post">
    {% csrf_token %}
        {{ form.as_p }}
        <p><input type="submit" value="登録"></p>
    </form>
</body>
</html>

View からは下記をキーとする辞書を受け取ることを期待した Template となっています。

  • 'form':ユーザーアカウント登録用のフォーム

ユーザーアカウント登録用の View の作成

ユーザーアカウント登録のための作業として、最後に View を作成していきたいと思います。

事前準備(プロジェクトやアプリの作成など)login_app/views.pysignup_view 関数を用意しましたので、この関数を変更することで、/login_app/signup/ へのリクエスト時のユーザーアカウント登録のページの表示、さらには送信されたフォームに従ったユーザーアカウントの登録を実現していきます。

具体的には、GET メソッド(POST メソッド以外)でリクエストされた際には、Template を描画してユーザーアカウント登録のページの表示を行うように signup_view 関数を変更していきます。

ユーザーアカウント登録ページへのGET時の処理の様子

この際は、先ほど作成したテンプレートを render 関数で描画すれば良いだけです。これにより、下記の4つの入力フィールドを持つフォームがページに表示されます。

  • 'username':ユーザー名
  • 'email':メールアドレス
  • 'password1':パスワード
  • 'password2':パスワード(確認用)

また、POST メソッドでリクエストされた際には、上記の4つの情報がデータとして送信されてきますので、これらのデータに基づいた情報を User モデルのインスタンスとしてデータベースに保存するように signup_view 関数を変更していきます。このデータベースへの保存によってユーザーアカウントが登録されることになります。

ユーザーアカウント登録ページへのPOST時の処理の様子

このデータベースの保存は、login_app/forms.py に定義した SignupForm のインスタンスを POST で送信されてきたデータから生成し、そのインスタンスに save メソッドを実行させることで実現できます。

ただし、単にデータベースに保存するだけではダメで、'username' が他のユーザーと重複している場合や 'password1''password2' が不一致している場合はデータベースへの保存を行わないようにする必要があります(ユーザーアカウントの登録に失敗するようにする)。

要は POST で送信されてきたデータの妥当性の検証が必要ということです。

この妥当性の検証は、通常の forms.ModelForm のサブクラスのフォームと同様に、POST で送信されてきたデータから生成したインスタンスに is_valid メソッドを実行させることで行うことが出来ます。

ですので、is_valid メソッドが true を返却した場合のみ save メソッドを実行させるようにすれば、妥当でないユーザーアカウントの登録を防ぐことができるようになります('username' が他のユーザーと重複している場合や 'password1''password2' が不一致している場合)。

上記の解説内容を考慮して作成した signup_view 関数は下記のようになります(SignupFormimport が必要である点に注意してください)。

ユーザーアカウントの登録
from django.shortcuts import render
from .forms import SignupForm

def signup_view(request):
    if request.method == 'POST':

        form = SignupForm(request.POST)
        if form.is_valid():
            form.save()

    else:
        form = SignupForm()
    
    param = {
        'form': form
    }

    return render(request, 'login_app/signup.html', param)

略

上記の signup_view 関数を見ていただければ分かる通り、ユーザーアカウントの登録に成功しても再度ユーザーアカウント登録ページが表示されることになります。

なので、ちょっとユーザーアカウントの登録に成功したことが分かりにくいです…。

上記では実装を簡単にするためにこのような動作になっていますが、ユーザーアカウント登録成功時に続けてログイン処理を行うことも可能です。この実例は最後の ログイン機能実現用のスクリプト で紹介していますので、興味があればそちらを参考にしていただければと思います。

ユーザーアカウント登録の動作確認

以上によりユーザーアカウント登録をができるようになったはずなので、ここで動作確認をしておきましょう!

まずはいつも通り下記コマンドで Django の開発用ウェブサーバーを起動させましょう。

% python manage.py runserver

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

http://localhost:8000/login_app/signup/

ここまで解説してきた通りに変更をしてきてくださった方であれば、下の図のようなページが表示されるはずです。この入力フォーム部分が SignupForm から作成したフォームになります。

表示されるユーザーアカウント登録ページ

さらに、上の入力フォームから順に「ユーザー名」「メールアドレス」「パスワード」「パスワード(確認用)」を入力して 登録 ボタンを押下すれば、ユーザーアカウントの登録が行われます。

ただ、現状はユーザーアカウントの登録が行われてもページが遷移したりするわけではないため、本当に登録が行われたかどうかが確認しづらいと思います…。

ユーザーアカウントが登録されてるかどうかを確認したい場合は、データベースの auth_user テーブルの中身を確認してみてください。登録したユーザーのユーザー名が username の列に、メールアドレスが email の列に設定されているレコードが見つかるはずです(見つからない場合はユーザーアカウントの登録に失敗していると思われます)。

ユーザー登録によりauth_userテーブルにレコードが追加される様子

また、重複する「ユーザー名」を入力して登録しようとしたり、「パスワード」と「パスワード(確認用)」が異なる状態で登録しようとしたりした場合は下記のようなエラーメッセージが表示されるようになっており、この場合はユーザーアカウントの登録ができないようになっています。

A user with that username already exists.
The two password fields didn’t match.

以降の動作確認を行う際にはユーザーアカウントは複数存在した方が良いので、上記のフォームから3つ程度のユーザーアカウントの登録を行なっておいてください。

スポンサーリンク

ログイン

一応ユーザーアカウントが登録できるようになったので、次はログインを実現していきます。

ログインを実現する上で必要になるのが、ログインフォームの表示、さらにはログインフォームから送信されてきたユーザーの情報で認証を行い、認証 OK の場合にそのユーザーの状態をログイン状態に設定する処理となります。

認証 OK とは、送信されてきたユーザー名がユーザーアカウントとして登録されている&パスワードが正しいことを言います。

難しそうですが、Django に用意されている関数を利用すれば、これらも簡単に実現することが可能です。

ログイン用の Form の作成

まずはログインフォームを定義していきましょう!

ログインフォームに関しては、AuthenticationForm のサブクラスとしてフォームを定義することで簡単に実現することが出来ます(そのまま AuthenticationForm を利用するのでも問題ありませんが、今回は今後の拡張を見据えてログインフォームを別途作成するようにしています)。

AuthenticationForm は Django に標準で用意されているフォームであり、ユーザー名とパスワードの入力受付を行う認証用のフォームとなります。

例えば、下記のように login_app/forms.py を変更してやれば、ユーザー名とパスワードを入力してログインを行うフォームを定義することが出来ます(AuthenticationFormimport を忘れないように注意してください)。

ログイン用のフォーム
from django import forms
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm, AuthenticationForm

class SignupForm(UserCreationForm):
    class Meta:
        model = User
        fields = ['username', 'email', 'password1', 'password2']

class LoginForm(AuthenticationForm):
    pass

ログイン用の Template の作成

次に、ログイン用のフォームをページ上に表示するための Template を login_app/templates/login_app/login.html として作成していきたいと思います(新規作成が必要なファイルになります)。

ユーザーアカウント登録用の Template の作成 の時同様に、通常のフォームを表示する時と同様に、{{ form.as_p }} 等を利用すれば簡単にログインフォームを Template に組み込むことが出来ます。

今回は、下記のように login_app/templates/login_app/login.html を新規作成していきたいと思います。

ログイン用のTemplate
{% load static %}
<!doctype html>
<html lang="ja">
<head>
    <meta charset="utf-8">
    <title>ログイン</title>
</head>
<body>
    <h1>ログイン</h1>
    <form action="{% url 'login' %}" method="post">
    {% csrf_token %}
        {{ form.as_p }}
        <p><input type="submit" value="ログイン"></p>
    </form>
</body>
</html>

View からは下記をキーとする辞書を受け取ることを期待した Template となっています。

  • 'form':ログイン用のフォーム

ログイン用の View の作成

ログイン実現のための作業の最後として、続いて View を作成していきます。

事前準備(プロジェクトやアプリの作成など)login_app/views.pylogin_view 関数を用意しましたので、この関数を変更することで、/login_app/login/ へのリクエストがあった際のログインページの表示、さらにはフォームから送信されてきたユーザー名とパスワードの認証とログインの処理を実現していきます。

より具体的には、GET メソッド(POST メソッド以外)でリクエストされた際はアカウント登録時と同様に Template を描画してログインのページの表示を行うようにlogin_view 関数を変更していきます。

ログインページへのGET時の処理の様子

さらに、POST メソッドでリクエストされた際には送信されてきたユーザー名とパスワードで認証を行い、認証 OK の場合にユーザーをログイン状態に設定するようにしていきます。

ログインページへのPOST時の処理の様子

AuthenticationForm のサブクラスのフォームの場合、認証に関しては、このフォームのインスタンスに is_valid メソッドを実行させることで実現できます。

もう少し詳しくいうと、is_valid メソッドの中で authenticate という関数が実行され、送信されてきたユーザー名がユーザーアカウント登録されているか、さらにはパスワードが正しいかどうかを判断してくれます。

ユーザー名が登録されており、さらにパスワードが正しい場合、is_valid メソッドは True を返却しますので、この場合のみユーザーをログイン状態にしてやれば良いことになります。

さらに、ユーザーをログイン状態にする処理は login 関数を実行することで実現できます。

この login 関数を実行する際には、ログイン状態に移行させるユーザー(User モデルのインスタンス)を引数に指定する必要がありますが、このユーザーに関しては AuthenticationForm のサブクラスのフォームのインスタンスに get_user メソッドを実行させることで取得することが出来ます。

この辺りを考慮して変更を行った login_view 関数は下記のようになります(import をいくつか追加しているので注意してください)。

ログイン用のView
from django.shortcuts import render
from .forms import SignupForm, LoginForm
from django.contrib.auth import login

def signup_view(request):
    略

def login_view(request):
    if request.method == 'POST':
        form = LoginForm(request, data=request.POST)

        if form.is_valid():
            user = form.get_user()

            if user:
                login(request, user)

    else:
        form = LoginForm()

    param = {
        'form': form,
    }

    return render(request, 'login_app/login.html', param)

略

「認証」や「ログイン」と聞くと難しそうに感じると思いますが、実際の関数を見てみると思ったより簡単であると感じる方が多いのではないかと思います。こんな感じで、Django に用意されているクラスや関数を利用することで、ログイン等の様々な機能を簡単に実現することが可能です。

ただし、上記の login_view 関数で行われるのは認証とログインだけであり、本当はログイン後に別のページにリダイレクトするような処理が必要になります。

これについては後述の ログイン後のリダイレクト で解説していきます。

ログインの動作確認

ひとまずログイン自体はできるようになったため、ログインを実際に行ってみましょう!

まずは Django 開発用ウェブサーバーを起動後、ウェブブラウザで下記 URL を開いてみてください。

http://localhost:8000/login_app/login/

おそらく下の図のようなページが表示されるはずです。下側の入力フォームが LoginForm から作成したフォームになります。

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

続いて、UsernamePassword の入力欄に、ユーザーアカウント登録の動作確認 で登録したユーザーのユーザー名とパスワードを入力し、ログイン ボタンを押下してみてください。

正直ボタンを押下してもページが遷移しないのでログインできたことが分かりにくいと思いますが、データベースの auth_user のテーブルを確認することでログインできたことを確認することが出来ます。

具体的には、ログインを行なったユーザーに対応するレコードの last_login のフィールドに ログイン ボタンを押下した時の日時が格納されているはずです。last_login はユーザーが最終ログインした日時を格納するフィールドであり、ここに日時が格納されていることから、先程のログインボタン押下によってログインが行われたことを確認できると思います。

auth_userテーブルにlast_loginが追記される様子

さらに、ログインによって django_session のテーブルにレコードが追加されているはずです。

ログインによってdjango_sessionテーブルにレコードが追加される様子

このレコードは、ログイン状態のユーザーを見分けるための重要なデータとなります。

ウェブブラウザからウェブアプリに対して HTTP リクエストを送信する時には(ウェブアプリにアクセスする時には)、session_key がリクエストのヘッダーに設定されることになります。

そして、その設定された session_key と同じ値を持つレコードが django_session テーブルに存在する場合、その HTTP リクエストを送信してきたウェブブラウザのユーザーをログイン状態と判断するようになっています。

逆に session_key を持つレコードが django_session に存在しない場合は、ウェブブラウザのユーザーをログイン状態でないと判断します。

django_sessionテーブルの情報からアクセスしてきたユーザーがログイン中であるかどうかを判断する様子

このようにしてユーザーがログイン状態であるかどうかを判断するようになっているため、ログインが行われるたびにユーザーごとに django_session にレコードが追加される(or レコードが更新される)ようになっています。

ちなみに、次に説明する「ログアウト」を行うことで、ログアウトを行なったユーザーに対する django_session のレコードが削除されることになります。

現状だとデータベースの変化からしかログインされた状態が判断できないのでちょっと分かりにくいかもしれないですが、前述のデータベースの結果より、ここまでの変更でログインができるようになったことを確認できると思います。

スポンサーリンク

ログアウト

続いてはユーザーのログアウトを実現していきたいと思います。

ログイン時は、フォームから送信されてきたユーザー名やパスワードから認証を行い、認証 OK の場合のみ login 関数を実行するようにしていましたが、ログアウトの場合はログアウトページ表示時に単に logout 関数を実行すれば良いだけです。

なので、ログイン自体もそんなに大変ではなかったと思いますが、ログアウトはさらに簡単に実現することが出来ます。

ログアウト用の Template の作成

ということで、まずはログアウト用の Template を作成していきたいと思います。

今回はログアウトページの Template として、下記のような login_app/templates/login_app/logout.html を新規作成していきたいと思います。

ログアウト用のTemplate
{% load static %}
<!doctype html>
<html lang="ja">
<head>
    <meta charset="utf-8">
    <title>ログアウト</title>
</head>
<body>
    <p>ログアウトしました...</p>
</body>

</html>

ログアウト用の View の作成

続いて、ログアウト用の View を作成していきます。

最低限必要なのは、前述で紹介した logout 関数の実行と、Template の render 関数で描画くらいだと思います。

もちろん、ログアウト後にリダイレクトを行なって他のページに自動的に遷移させるようなことをしても良いと思いますが、今回は最低限必要な処理のみを行います。

このような View は、logout_view 関数を下記のように変更することで実現することが出来ます(logoutimport が必要である点に注意してください)。

ログアウト用の View
from django.shortcuts import render
from .forms import SignupForm, LoginForm
from django.contrib.auth import login, logout

def signup_view(request):
    略

def login_view(request):
    略

def logout_view(request):
    logout(request)

    return render(request, 'login_app/logout.html')

略

ログアウトの動作確認

以上によってログアウトが実現できるようになったため、ログアウトの動作確認を行なっていきたいと思います。

まずは Django 開発用ウェブサーバーを起動後、ログインの動作確認 に従ってユーザーログインを行っておいてください。

次に、データベースの django_session テーブルを確認してみてください。ログインを行なったことで django_session のレコードが追加されているはずです(もしくは更新されている)。

ログインによってdjango_sessionテーブルにレコードが追加される様子

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

http://localhost:8000/login_app/logout/

すると、下の図のような画面が表示されるはずです。

表示されるログアウトページ

さらに、再度 django_session テーブルを確認してみてください。先ほど存在していたレコードが削除されているはずです(複数存在していた場合は1つ減っている)。

ログアウトによってdjango_sessionテーブルからレコードが削除される様子

ログインの動作確認 で解説した通り、django_session のレコードはログインが行われるたびにユーザーごとに追加(or 更新)されるデータであり、ログアウトを行うことでログアウトしたユーザーに対するレコードが削除されることになります。

上記の結果より、django_session のレコードが削除されており、ユーザーのログアウトが行われていることを確認することができると思います。

参考:自動ログアウト

Django においては、一定時間が経過すると自動的にユーザーのログアウトが行われるようになっています。

上の図の django_session テーブルの expire_date フィールドの値が、ユーザーの自動ログアウトが行われる日時を示すものになります。おそらくログインしてから2週間後にログアウトされるように日時が格納されているはずです。

この自動ログアウトまでの時間は変更することが可能で、この変更方法は下記ページで解説していますので、詳しく知りたい方は是非下記ページを読んでみてください。

Djangoにおける自動ログアウトまでの時間の変更方法解説ページアイキャッチ 【Django】自動ログアウト時間の設定方法

ログインユーザーに応じたページ表示

続いて、ログインユーザーに応じたページの表示を実現していきたいと思います。

ログインユーザーに応じてページを表示するためには、まずはページを表示しているユーザーのオブジェクト(User モデルのインスタンス)を取得する必要があります。

既にログイン中の場合、このユーザーのオブジェクトは View の関数に引数として渡される requestuser データ属性から取得することが出来ます。

MEMO

詳しく説明すると、ページ表示時にはウェブブラウザからサーバーに対して HTTP リクエストが送信されますが、そのウェブブラウザでログインしている場合、そのリクエストのヘッダーには先程紹介した session_key がセットされるようになります

そして、その session_key から Django 本体によってユーザーのオブジェクトが取得され、それが request.uesr にセットされた状態で View 関数が実行されるようになります

そのため、View の request.user から User モデルのフィールドに設定されているデータ(usernameemail など)を取得し、それをページに表示してやれば、ログインユーザーに応じたページの表示を実現することができることになります。

今回はユーザーを管理するモデルとして User をそのまま利用していますが、ユーザーを管理するモデルを自身でカスタマイズしてアプリに応じた情報をモデルのフィールドに設定できるようにしておけば、上記のような手順でさらに様々な情報をページに表示することも可能になります(例えばユーザーのアバターを表示したり、ユーザーの投稿した画像を表示したり)。

ログインユーザーに応じたページ表示用の Template の作成

では実際にログインユーザーに応じたページの表示を行なっていきましょう!

まずは Template から作成していきたいと思います。

今回は、ログインユーザーに応じた情報を表示するページとして2つのページを用意していきたいと思いますので、2つの Template を作成していきます。

1つ目はログインユーザーの情報(ユーザー名とメールアドレスとアカウント登録日)を表示するページの Template とし、2つ目はログインユーザー以外のユーザーの情報(ユーザー名とメールアドレス)を表示するページの Template としたいと思います。

まずは1つ目の Template として、下記のように login_app/templates/login_app/user.html を新規作成します。

ユーザーの情報表示を行うTemplate
{% load static %}
<!doctype html>
<html lang="ja">
<head>
    <meta charset="utf-8">
    <title>あなたの情報</title>
</head>
<body>
    <h1>{{user.username}}</h1>
    <p>連絡先:{{user.email}}</p>
    <p>アカウント作成日:{{user.date_joined}}</p>
</body>

</html>

次に2つ目の Template として、下記のように login_app/templates/login_app/other.html を新規作成します。

他のユーザーの情報表示を行うTemplate
{% load static %}
<!doctype html>
<html lang="ja">
<head>
    <meta charset="utf-8">
    <title>他のユーザーの情報</title>
</head>
<body>
    <h1>他のユーザーの情報</h1>
    {% for user in users %}
    <h2>{{user.username}}</h2>
    <p>連絡先:{{user.email}}</p>
    {% endfor %}
</body>

</html>

user.html ではログインユーザーの情報のみを表示するため、User モデルのインスタンスを user として受け取ることを期待する Template となっています。

そして、user のデータ属性(username など)をページに組み込む形の Template となっています。

それに対し、other.html ではログインユーザー以外のユーザーの情報を表示するため、複数の User モデルのインスタンスが格納された listquerysetusers として受け取ることを期待する Template となっています。

そして、for 文の中で1つ1つの User モデルのインスタンスを user として取得し、user のデータ属性(username など)をページに組み込む形の Template となっています。

ログインユーザーに応じたページ表示用の View の作成

続いて View を作成していきます。

まず、前述の通り、ログインユーザーのオブジェクトは request.user にセットされていますので、ログインユーザーの情報をページに表示するためには、これをページに組み込む Template に request.userを渡してやれば良いことになります。

そのため、ログインユーザー自体の情報を表示する際の Vew は、user_view 関数を下記のように変更することで実現することが出来ます。

ユーザーの情報表示を行うView
from django.shortcuts import render
from .forms import SignupForm, LoginForm
from django.contrib.auth import login, logout

def signup_view(request):
    略

def login_view(request):
    略

def user_view(request):
    user = request.user

    params = {
        'user': user
    }

    return render(request, 'login_app/user.html', params)

略

ただし、request.user にセットされているのはあくまでもログインユーザーのオブジェクトであり、他のユーザーの情報は request.user から取得することはできません。

ですので、ログインユーザー以外の情報をページに表示したい場合は、データベースから別途ログインユーザー以外のオブジェクト(User モデルのインスタンス)を取得する必要があります。

上記を考慮して変更を行なった other_view 関数は次のようになります。Userimport が必要である点に注意してください。

他のユーザーの情報表示を行うView
from django.shortcuts import render
from .forms import SignupForm, LoginForm
from django.contrib.auth import login, logout
from django.contrib.auth.models import User

def signup_view(request):
    略

def login_view(request):
    略

def user_view(request):
    略

def other_view(request):
    users = User.objects.exclude(username=request.user.username)

    params = {
        'users': users
    }

    return render(request, 'login_app/other.html', params)

ログインユーザー以外のオブジェクトを取得しているのが User.objects.exclude の部分になります(username フィールドが request.user.username と異なる User モデルのオブジェクトを取得している)。

また、この exclude の返却値は User モデルの queryset であり、これをそのまま Template に渡すようにしています。これにより、other.htmlfor 文の中で1つ1つの User モデルのインスタンスの情報が Template で組み込まれることになります。

ログインユーザーに応じたページ表示の動作確認

以上で、簡単ですがログインユーザーに応じたページ表示ができるようになったことになりますので動作確認をしておきましょう!

まずは Django の開発用ウェブサーバーを起動し、ログインの動作確認 に従ってユーザーのログインを行なっておいてください。

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

http://localhost:8000/login_app/user/

開くと、ログイン中のユーザーのユーザー名とメールアドレス、アカウント登録日時が表示されることを確認することができると思います。

ログインユーザーの情報が表示される様子

さらに、今度はウェブブラウザで下記 URL を開いてみてください。

http://localhost:8000/login_app/other/

そうすると、ログイン中のユーザー以外のユーザー名とメールアドレスが一覧表示されることを確認することができると思います。

ログインユーザー以外のユーザーの情報が表示される様子

次は、ログインの動作確認 に従って先ほどは異なるユーザーでログインを行い、その後に再度ウェブブラウザで下記 URL を開いてみてください。

http://localhost:8000/login_app/user/

ウェブブラウザからログインしているユーザーが変わったので、今度は先ほどとは異なる情報が表示されていることが確認できると思います。

先ほどとは異なるログインユーザーの情報が表示される様子

同様に、下記 URL を開いた場合も先ほどとは異なる情報が表示されていることが確認できると思います。

http://localhost:8000/login_app/other/

このような結果から、ここまで解説してきた内容に基づいて View や Template を作成すれば、ログイン中のユーザーに応じたページ表示を実現することが可能であることを理解していただけるのではないかと思います。

未ログインユーザーからのアクセス禁止

ただし、現状では未ログインユーザーであっても下記のページを閲覧することが可能です。

  • http://localhost:8000/login_app/user/
  • http://localhost:8000/login_app/other/

つまり、ログインしなくてもアプリが利用可能ということです。

ただ、特に会員制のウェブアプリを開発する上では、未ログインユーザーから閲覧されたくないページもあると思います。

そのため、続いては未ログインユーザーに特定のページへのアクセスを禁止させる方法について解説します。

まず、アクセス禁止はリダイレクトを利用して実現します。要は、未ログインユーザーから特定のページへのアクセスがあった場合にリダイレクトを行なって他のページに自動的に遷移するようにさせます。

リダイレクトによってアクセス禁止を制御する様子

さらに、この未ログインユーザーからの特定のページへのアクセスがあった際のリダイレクトは、そのページに対応する View の関数に login_required デコレータを指定することで実現することが出来ます。

また、リダイレクト先のページは settings.pyLOGIN_URL を指定することで好きなページに設定することが出来ます。

例えば LOGIN_URL にログインページのパスを指定しておけば、未ログインユーザーから特定のページのアクセスがあった際に自動的にログインページに遷移させることが出来ます。

未ログインユーザーからのアクセス禁止用の View の設定

では、実際に未ログインユーザーからのアクセスがあった際に、ログインページにリダイレクトを行うことでそのページにアクセスできないようにしていきたいと思います。

まずは login_app/view.py において、下記のように未ログインユーザーからのアクセスを禁止したいページに対応する View の関数の前に @login_required を指定するようにします。login_requiredimport が必要な点に注意してください。

アクセス禁止の設定
from django.shortcuts import render
from .forms import SignupForm, LoginForm
from django.contrib.auth import login, logout
from django.contrib.auth.models import User
from django.contrib.auth.decorators import login_required

def signup_view(request):
    略

def login_view(request):
    略

def logout_view(request):
    略

@login_required
def user_view(request):
    略

@login_required
def other_view(request):
    略

上記の変更により、アクセスしてきたのが未ログインユーザーの場合、user_viewother_view が実行されるのではなくリダイレクトが行われるようになります。

つまり、次の2つのページへの未ログインユーザーのアクセスを禁止することが出来ます。

  • http://localhost:8000/login_app/user/
  • http://localhost:8000/login_app/other/

未ログインユーザーからのアクセス禁止用の settings.py の設定

さらに、login_project/settings.py に LOGIN_URL を指定することで、リダイレクト先のページの設定を行います。

今回は LOGIN_URL/login_app/login/ を指定することで、未ログインユーザーからのアクセス時にログインページにリダイレクトするようにしたいと思います。

これは、login_project/settings.py に下記のように LOGIN_URL の指定を追記することで実現することが出来ます。追記する場所は login_project/settings.py の最後部分で良いと思います。

リダイレクト先の設定
LOGIN_URL = '/login_app/login/'

未ログインユーザーからのアクセス禁止の動作確認

では、未ログインユーザーからのアクセスが本当に禁止されるようになったかを確認しておきましょう!

まずは、ログアウトの動作確認 の手順に従って事前にログアウトを行なっておいてください。

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

http://localhost:8000/login_app/user/

すると、自動的にログインページが表示されるようになったことを確認することができると思います。

リダイレクトによってログインページに自動的に遷移する様子

続いて、表示されたログインページでログインを行なった後に、再度上記 URL をウェブブラウザで開いてみてください。

すると、今度はログインページではなく、ログインしたユーザーの情報が表示されて上記 URL にアクセスできるようになったことが確認できると思います。

下記の URL を開いた際にも同様のことが確認できるはずです。

http://localhost:8000/login_app/other/

これらの結果より、@login_required を利用することで未ログインユーザーのアクセスを禁止することが可能であることが確認できると思います。

スポンサーリンク

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

ここまでの実装によってウェブアプリにログイン機能を持たせることが出来たことになります。

ただ、現状ではログインを行なってもそのままログインページが表示されるだけなので、ウェブアプリの動作としてイマイチです。

次は、ログイン後に別のページにリダイレクトさせるようにしていきたいと思います。

リダイレクト先を固定のページとする

リダイレクト先が固定で良いのであれば、ログインを行う View の関数の中で、login 実行後に redirect 関数の実行結果を return してやれば良いだけです。

redirect実行時には to 引数にリダイレクト先のパスや URL を指定します。

例えば、下記のように login_app/views.pylogin_view を変更してやれば、ログイン後に http://localhost:8000/login_app/user/ にリダイレクトされるようになります(redirectimport が必要である点に注意してください)。

固定ページへのリダイレクト
from django.shortcuts import render, redirect
from .forms import SignupForm, LoginForm
from django.contrib.auth import login, logout
from django.contrib.auth.models import User
from django.contrib.auth.decorators import login_required

def signup_view(request):
    略

def login_view(request):
    if request.method == 'POST':
        form = LoginForm(request, data=request.POST)

        if form.is_valid():
            user = form.get_user()

            if user:
                login(request, user)
                return redirect(to='/login_app/user/')

    else:
        form = LoginForm()

    param = {
        'title': 'ログイン',
        'form': form,
    }

    return render(request, 'login_app/login.html', param)

略

リダイレクト先を元々のアクセスページとする

また、ログイン後のリダイレクト先を、元々ユーザーがアクセスしようとしていたページとすることもできます。

未ログインユーザーからのアクセス禁止 での @login_required の対応によって、未ログインの状態で下記ページにアクセスした場合、ログインページにリダイレクトされるようになりました。

  • http://localhost:8000/login_app/user/
  • http://localhost:8000/login_app/other/

例えば前者のページにアクセスしようとしてログインページにリダイレクトされた場合、ログイン後には前者のページにリダイレクトされた方がユーザーにとって便利ですし、後者のページにアクセスしようとしてリダイレクトされた場合は、ログイン後には後者のページにリダイレクトされた方が便利です。

このように、@login_requiredによってログインページにリダイレクトされた場合、固定のページではなく、元々ユーザーがアクセスしようとしていたページにリダイレクトを行なった方が良い場合も多いです。

次は、ログイン後のリダイレクト先を「元々ユーザーがアクセスしようとしていたページ」にするための方法を解説していきます。

まずポイントになるのが、未ログインユーザーがログインページにリダイレクトされる際の URL になります。

実際に試していただければ分かると思いますが、例えば未ログインの状態で http://localhost:8000/login_app/user/ にアクセスした場合はログインページにリダイレクトされることになりますが、このリダイレクト先の URL にはクエリストリングとして next パラメータが付与されることになります(@login_required によって付与される)。

そして、この next パラメータの値として、元々ユーザーがアクセスしようとしていたページのパスが設定されています。

そのため、ログイン後のリダイレクト先を next パラメータの値のパスとしてやれば、ログイン後のリダイレクト先を元々ユーザーがアクセスしようとしていたページとすることが出来ます。

ログイン後のリダイレクト先を next パラメータの値のパスに設定するためには、まず login_app/views.pylogin_view 関数を下記のように変更し、

nextの指すページへのリダイレクト
from django.shortcuts import render, redirect
from .forms import SignupForm, LoginForm
from django.contrib.auth import login, logout
from django.contrib.auth.models import User
from django.contrib.auth.decorators import login_required

def signup_view(request):
    略

def login_view(request):
    if request.method == 'POST':
        next = request.POST.get('next')
        form = LoginForm(request, data=request.POST)

        if form.is_valid():
            user = form.get_user()

            if user:
                login(request, user)
                if next == 'None':
                    return redirect(to='/login_app/user/')
                else:
                    return redirect(to=next)
    else:
        form = LoginForm()
        next = request.GET.get('next')

    param = {
        'form': form,
        'next': next
    }

    return render(request, 'login_app/login.html', param)

略

さらに login_app/templates/login_app/login.html を下記のように変更します(input タグを1つ追加しています)。

nextパラメータの送信
{% load static %}
<!doctype html>
<html lang="ja">
<head>
    <meta charset="utf-8">
    <title>ログイン</title>
</head>
<body>
    <h1>ログイン</h1>
    <form action="{% url 'login'%}" method="post">
    {% csrf_token %}
        {{ form.as_p }}
        <p><input type="hidden" name="next" value="{{next}}"></p>
        <p><input type="submit" value="ログイン"></p>
    </form>
</body>
</html>

login.html 関数では、View から渡された next をユーザー名やパスワードと一緒に送信を行うように変更しています。

これにより、ログインボタンが押下されて login_view 関数がメソッド POST で実行される際に、request.POST['next']next パラメータの値のパスが設定されるようになります。

また、login_view 関数は大きく2つの変更をしており、1つ目は上記の next パラメータの値を Template に渡すための変更になります(メソッドが POST 以外の場合の処理)。

さらに、メソッドが POST の場合は、送信されてきた nextパラメータの値を redirect の引数に設定するようにしています。これにより、ログイン時に元々ユーザーがアクセスしようとしていたページにリダイレクトが行われるようになります。

ただし、next パラメータが設定されていないような場合もあるため(直接ログインページにアクセスされた場合)、その場合は固定のページにリダイレクトするようにしています。

ログイン後のリダイレクトの動作確認

最後に リダイレクト先を元々のアクセスページとする で紹介したスクリプトを用いて、ログイン後のリダイレクトの動作確認を行なっていきたいと思います。

まずは Django の開発用ウェブサーバーを起動し、ログアウトの動作確認 に従ってユーザーのログアウトを行なっておいてください。

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

http://localhost:8000/login_app/user/

開くとリダイレクトが行われて下の図のログインページが表示されると思います。

リダイレクトされてログイン画面が表示される様子

ポイントは、上の図のように、ウェブブラウザのアドレスバー部分の URL の ?next= 以降に元々アクセスしようとしていたページのパスが表示されているところになります。

さらに、登録済みのユーザー名とパスワードを入力してログインを行なってみてください。

ログインに成功した場合、?next= 以降で示されていたパスに自動的にリダイレクトされるはずです。

ログインユーザーの情報が表示される様子

続いて、再度 ログアウトの動作確認 に従ってユーザーのログアウトを行い、次はウェブブラウザで下記 URL を開いてみてください。

http://localhost:8000/login_app/other/

開くと、先ほど同様にログインページが表示されますが、先ほどとは URL の ?next= 以降のパスが変化していることが確認できるはずです。これは、先ほどとは元々アクセスしようとしていたページが異なるからです。

/login_app/other/からリダイレクトされてログイン画面が表示される様子

さらに、再度登録済みのユーザー名とパスワードを入力してログインを行なってみてください。ログインに成功した場合、?next= 以降で示されていたパスに自動的にリダイレクトされるはずです。

ログインユーザー以外のユーザーの情報が表示される様子

最後に、再度 ログアウトの動作確認 に従ってユーザーのログアウトを行い、次はウェブブラウザで下記 URL を開いて直接ログインページにアクセスしてみてください。

http://localhost:8000/login_app/login/

当然ログインページが表示されますが、ここまでの動作結果とは異なり、URL に ?next= が存在しないことを確認できると思います。このように直接ログインページにアクセスされた場合は URL に ?next= は存在しません。

直接アクセスしてログイン画面が表示される様子

ただ、この場合であっても、ログインを行えば /login_app/user/ にリダイレクトされることが確認できるはずです。

3つのパターンの動作確認を行いましたが、いずれの場合であっても、ログイン後のリダイレクトによってアプリの利便性が向上したことを確認できると思います。

ログイン機能実現用のスクリプト

以上で、ログイン機能を実現するために必要な実装についての解説は終了です。

ここまで各動作を実現するために個別にスクリプトを紹介してきましたので、ここで forms.pyviews.py・各種テンプレートファイルをまとめて紹介しておきます。

forms.py はここまで紹介したものと全く同じです。ですが、views.py やテンプレートに関しては、ここまで紹介してきたものに比べて下記を変更して多少アプリを使用しやすくしていますので、これらの点も参考にしていただければと思います。

  • views.py:ユーザーアカウント登録時にログインを同時に行うように変更
  • テンプレート:他のページへのリンクを追加

他のファイル等の作成に関しては 事前準備(プロジェクトやアプリの作成など) で紹介していますので、まだ読んでいない方は こちら を参考にしていただければと思います(settings.py に関しては 未ログインユーザーからのアクセス禁止用の settings.py の設定 で紹介した変更も必要なので注意してください)。

login_app/forms.py
from django import forms
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm, AuthenticationForm

class SignupForm(UserCreationForm):
    class Meta:
        model = User
        fields = ['username', 'email', 'password1', 'password2']

class LoginForm(AuthenticationForm):
    pass
login_app/views.py
from django.shortcuts import render, redirect
from .forms import SignupForm, LoginForm
from django.contrib.auth import login, logout
from django.contrib.auth.models import User
from django.contrib.auth.decorators import login_required

def signup_view(request):
    if request.method == 'POST':

        form = SignupForm(request.POST)
        if form.is_valid():
            user = form.save()
            login(request, user)
            return redirect(to='/login_app/user/')

    else:
        form = SignupForm()
    
    param = {
        'form': form
    }

    return render(request, 'login_app/signup.html', param)

def login_view(request):
    if request.method == 'POST':
        next = request.POST.get('next')
        form = LoginForm(request, data=request.POST)

        if form.is_valid():
            user = form.get_user()

            if user:
                login(request, user)
                if next == 'None':
                    return redirect(to='/login_app/user/')
                else:
                    return redirect(to=next)
    else:
        form = LoginForm()
        next = request.GET.get('next')

    param = {
        'form': form,
        'next': next
    }

    return render(request, 'login_app/login.html', param)

def logout_view(request):
    logout(request)

    return render(request, 'login_app/logout.html')

@login_required
def user_view(request):
    user = request.user

    params = {
        'user': user
    }

    return render(request, 'login_app/user.html', params)

@login_required
def other_view(request):
    users = User.objects.exclude(username=request.user.username)

    params = {
        'users': users
    }

    return render(request, 'login_app/other.html', params)

login_app/templates/login_app/signup.html
{% load static %}
<!doctype html>
<html lang="ja">
<head>
    <meta charset="utf-8">
    <title>ユーザー登録</title>
</head>
<body>
    <h1>ユーザー登録</h1>
    <form action="{% url 'signup' %}" method="post">
    {% csrf_token %}
        {{ form.as_p }}
        <p><input type="submit" value="登録"></p>
    </form>
</body>
</html>
login_app/templates/login_app/login.html
{% load static %}
<!doctype html>
<html lang="ja">
<head>
    <meta charset="utf-8">
    <title>ログイン</title>
</head>
<body>
    <h1>ログイン</h1>
    <form action="{% url 'login'%}" method="post">
    {% csrf_token %}
        {{ form.as_p }}
        <p><input type="hidden" name="next" value="{{next}}"></p>
        <p><input type="submit" value="ログイン"></p>
    </form>
    <p><a href="{% url 'signup'%}">ユーザー登録</a></p>
</body>
</html>
login_app/templates/login_app/logout.html
{% load static %}
<!doctype html>
<html lang="ja">
<head>
    <meta charset="utf-8">
    <title>ログアウト</title>
</head>
<body>
    <p>ログアウトしました...</p>
    <p><a href="{% url 'login'%}">ログイン</a></p>
</body>

</html>
login_app/templates/login_app/user.html
{% load static %}
<!doctype html>
<html lang="ja">
<head>
    <meta charset="utf-8">
    <title>あなたの情報</title>
</head>
<body>
    <h1>{{user.username}}</h1>
    <p>連絡先:{{user.email}}</p>
    <p>アカウント作成日:{{user.date_joined}}</p>
    <p><a href="{% url 'other'%}">他のユーザーの情報</a></p>
    <p><a href="{% url 'logout'%}">ログアウト</a></p>
</body>

</html>
login_app/templates/login_app/other.html
{% load static %}
<!doctype html>
<html lang="ja">
<head>
    <meta charset="utf-8">
    <title>他のユーザーの情報</title>
</head>
<body>
    <h1>他のユーザーの情報</h1>
    {% for user in users %}
    <h2>{{user.username}}</h2>
    <p>連絡先:{{user.email}}</p>
    {% endfor %}
    <p><a href="{% url 'user'%}">あなたの情報</a></p>
    <p><a href="{% url 'logout'%}">ログアウト</a></p>
</body>

</html>

まとめ

このページでは、Django におけるログイン機能の実現方法について解説しました!

ログイン機能を実現する上でメインになるのは当然「ログイン処理」になりますが、アプリにログイン機能を持たせる意味を考慮すると、下記の6つくらいも同時に実現した方が良いと思います。

  • ユーザーアカウントの登録
  • ログイン
  • ログイン後のリダイレクト
  • 未ログインユーザーからのアクセス禁止
  • ログインユーザーに応じたページ表示
  • ログアウト

それぞれ難しそうな処理にも思えますが、基本的には Django に用意されている関数やクラスを利用すれば簡単に実現可能です。

ウェブアプリにログイン機能を持たせることができれば、開発できるウェブアプリの幅を大きく広げることが出来ます!ウェブアプリを開発していきたいという方は、是非このページで紹介した内容は理解しておいてください!

また、今回は View を関数ベースでログイン機能を実現しましたが、ページの冒頭でも述べたように、LoginView などの Django に用意されているクラスを継承しながらクラスベースで View を作成することで更に簡単にログイン機能を実現することも出来ます。

その具体的な方法に関しては下記ページで解説していますので、クラスベースの View でログイン機能を実現したい場合は是非読んでみてください!

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

また、ユーザーのカスタマイズの仕方についても下記ページで解説していますので、User を使わずに独自のカスタムユーザーを利用してアプリ開発を行いたい場合は参考にしてください!

【Django】カスタムユーザー(独自のユーザー)の作り方【AbstractUser編】 カスタムユーザーをAbstractBaseUserを継承して作成する手順の解説ページアイキャッチ 【Django】カスタムユーザー(独自のユーザー)の作り方【AbstractBaseUser編】

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