Open1

(準備中)【Django】簡単なログインフォーム実装を通して、関数ベースビューのログイン機能を学ぶ

中井圭輔中井圭輔

はじめに

私はログインに取っつきづらさを感じていたのだが、意外と簡単だということに気づいた。そこで勉強のため、下記のサイトを参考にアプリをつくることにした。

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

こちらでログイン機能について詳しく説明しているので、はっきり言って私の記事は必要ない。しかし読んだだけでは身につかないということで、練習のために記事を書いた。

つくるもの

  • ログインとログアウトくらいしかできないアプリをつくる。
  • ログインへの理解に集中したいので、CSSなどは使わない。

ユーザー登録画面

ユーザー名とメールアドレス、パスワード、パスワード(確認用)を入力して、ユーザー登録を行う。
(TODO: 画面の画像)

ログイン画面

登録した情報を入力してログインする。
(TODO: 画面の画像)

メイン画面

アプリの本体。Hello Login!という素晴らしい文字が表示される。
(TODO: 画面の画像)

ログアウト画面

ログアウトしたことを知らせる。
(TODO: 画面の画像)

初期設定

Django用のディレクトリ作成

  • 任意の場所にDjango用のディレクトリをつくる。ここではdjangoという名前にした。
  • VSCodeを開く>FileOpen Folderdjangoを選択してディレクトリを開く。

仮想環境作成(任意)

仮想環境をつくる。(仮想環境名)には任意の名前を入力する。ここではvenv

> python -m venv (仮想環境名)

仮想環境を有効化する。有効化されるとコマンドラインの先頭に環境名が表示される

> venv/Scripts/activate
(venv)>

Djangoインストール

(venv)> pip install django

プロジェクト作成

Django プロジェクトを作成する。(プロジェクト名)には任意の名前を入力する。ここではconfig

(venv)> django-admin startproject (プロジェクト名) .

アプリケーション作成

アプリケーションを作成する。(アプリ名)には任意の名前を入力する。ここではlogin_app

(venv)> python manage.py startapp (アプリ名)

設定ファイル編集

django/config/settings.pyを編集する。

先ほど作成したアプリケーションを登録する。

django/config/settings.py
  INSTALLED_APPS = [
      "django.contrib.admin",
      "django.contrib.auth",
      "django.contrib.contenttypes",
      "django.contrib.sessions",
      "django.contrib.messages",
      "django.contrib.staticfiles",
+     "login_app",
  ]

言語設定を変更する。

django/config/settings.py
-  LANGUAGE_CODE = "en-us"
+  LANGUAGE_CODE = "ja"

タイムゾーンを変更する。

django/config/settings.py
-  TIME_ZONE = "UTC"
+  TIME_ZONE = "Asia/Tokyo"

動作確認

(venv)> python manage.py runserver 8000

http://127.0.0.1:8000/にアクセスすると、いつものロケットが表示される。

(TODO: ロケットの画面)

メイン画面

ログイン・ログアウトの前に、メイン画面をつくる。

View作成

views.pyファイルを以下のように編集する。render()でテンプレートを表示するだけである。

django/login_app/views.py
from django.shortcuts import render


def index(request):
    return render(request, "login_app/index.html")

URL設定

configディレクトリ内のurls.pyファイルを以下のように編集する。

django/config/urls.py
  from django.contrib import admin
- from django.urls import path
+ from django.urls import include, path

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

login_appディレクトリにurls.pyファイルを新規作成し、以下を入力する。

django/login_app/urls.py
from django.urls import path

from . import views

urlpatterns = [
    path("", views.index),
]

Template作成

login_appディレクトリに以下のような階層でディレクトリとファイルをつくる。

  django
   └── login_app
        ├── views.pyとか
+       └── templates
+            └── login_app
+                 └── index.html

index.htmlファイルに以下を入力する。Hello Login!を表示するだけである。

django/login_app/templates/login_app/index.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <title>メイン画面</title>
  </head>
  <body>
    <h1>Hello Login!</h1>
  </body>
</html>

動作確認

http://127.0.0.1:8000/login_app/にアクセスすると、Hello Login!が表示される。
(TODO: メイン画面)

ユーザー登録

Modelの作成(省略)

ユーザー登録には名前やパスワードなどのユーザー情報を保存するテーブルが必要である。いつもならテーブルを生成するためにModelをつくるが、ここではDjango標準のUser Modelを使用する。公式はUserの使用を推奨していないようだが、個人の勉強ということで...

マイグレーション

(venv)> python manage.py migrate

Form作成

login_appディレクトリにforms.pyファイルを新規作成し、以下を入力する。UserCreationFormが肝である。

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


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

View作成

views.pyファイルに以下を追加する。

  • POSTメソッドのときはis_valid()でデータの妥当性を検証してからsave()で保存する。
  • GETメソッド(POSTメソッド以外)のときは入力フォームを表示する。
django/login_app/views.py
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)

URL設定

urls.pyファイルに以下を追加する。

django/login_app/urls.py
urlpatterns = [
    ⋮
    path("signup/", views.signup_view, name="signup"),
]

Template作成

templates/login_appディレクトリにsignup.htmlファイルをつくり、以下を入力する。

  • {{ form.as_p }}に入力フォームが表示される。
  • {{ form }}だけでも入力フォームを表示できるが、.as_pをつけることで各入力項目を<p>タグで囲むことができる。必須ではないが、こちらの方が見やすい。
django/login_app/templates/login_app/signup.html
<!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 }}
      <input type="submit" value="登録" />
    </form>
  </body>
</html>

動作確認

http://127.0.0.1:8000/login_app/signup/にアクセスすると、ユーザー登録画面が表示される。各項目に入力し登録ボタンを押すと、ユーザー登録が行われる。

(TODO: ユーザー登録画面の画像)

テーブルの中身を確認

今回の実装では、登録しても画面が変わらない。このままでは登録できているか確認しづらいので、VSCodeの拡張機能SQLite Viewerでチェックする。

  1. 拡張機能SQLite Viewerをインストールする。
  2. db.sqlite3ファイルを開く。
  3. auth_userに先ほど登録した情報が表示される。すげえ!

(TODO: db.sqlite3の画像)

ログイン

Form作成

forms.pyに以下を追加する。AuthenticationFormが肝である。これを継承すればログインフォームを表示できる。

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

⋮

class LoginForm(AuthenticationForm):
    pass

View作成

views.pyファイルに以下を追加する。

  • POSTメソッドのときは認証を行う。認証OKのときだけlogin関数を実行する。
  • GETメソッド(POSTメソッド以外)のときはログインページを表示する。
django/login_app/views.py
from django.contrib.auth import login
from django.shortcuts import render

from .forms import LoginForm, SignupForm

⋮

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/")
    else:
        form = LoginForm()
    param = {"form": form}
    return render(request, "login_app/login.html", param)

URL設定

urls.pyファイルに以下を追加する。

django/login_app/urls.py
urlpatterns = [
    ⋮
    path("login/", views.login_view, name="login"),
]

Template作成

templates/login_appディレクトリにlogin.htmlファイルをつくり、以下を入力する。

  • {{ form.as_p }}にログインフォームが表示される。
django/login_app/templates/login_app/login.html
<!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 }}
      <input type="submit" value="ログイン" />
    </form>
  </body>
</html>

動作確認

  • http://127.0.0.1:8000/login_app/login/にアクセスすると、ログイン画面が表示される。
  • 先ほど登録したユーザー情報を入力してログインボタンを押すと、メイン画面に遷移する。
  • auth_userテーブルのlast_loginを確認すると値が入っているはず。last_loginは最終ログインの日時である。
  • session_keyテーブルを確認すると、値が入っている。

ログアウト

View作成

views.pyファイルに以下を追加する。logout()を実行してrender()でテンプレートを表示するだけである。

django/login_app/views.py
from django.contrib.auth import login, logout
from django.shortcuts import render

from .forms import LoginForm, SignupForm

⋮

def logout_view(request):
    logout(request)
    return render(request, "login_app/logout.html")

URL設定

urls.pyファイルに以下を追加する。

django/login_app/urls.py
urlpatterns = [
    ⋮
    path("logout/", views.logout_view, name="logout"),
]

Template作成

templates/login_appディレクトリにlogout.htmlファイルをつくり、以下を入力する。ログアウトしたことを示すだけである。

django/login_app/templates/
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <title>ログアウト</title>
  </head>
  <body>
    <p>ログアウトしました</p>
  </body>
</html>

動作確認

  • http://127.0.0.1:8000/login_app/logout/にアクセスすると、ログアウト画面が表示される。
  • django_sessionテーブルを確認すると、レコードが消えているはずである。

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

ログイン・ログアウトを実装したが、それらとメイン画面の紐づけをまだ行っていない。したがってログインしていない状態でも、今のところメイン画面のHello Login!を表示することができる。ログインが必要なページに未ログインユーザーがアクセスした場合、他のページへリダイレクトするよう編集する。

View編集

views.pyファイルに以下を追加する。関数に@login_requiredをつけるだけ。

django/login_app/views.py
+ from django.contrib.auth.decorators import login_required
  from django.shortcuts import render


+ @login_required
  def index(request):
      return render(request, "login_app/index.html")

リダイレクト先を設定

settings.pyに以下を追加する。LOGIN_URLにリダイレクト先のページを指定する。

django/config/settings.py
 + LOGIN_URL = '/login_app/login/'

動作確認

  • まずhttp://127.0.0.1:8000/login_app/logoutでログアウトする。
  • それからhttp://127.0.0.1:8000/login_app/にアクセスすると、メイン画面ではなくログイン画面にリダイレクトするはずである。

ユーザーに応じたページ処理

ログインの目的には「未ログインユーザーのアクセスを禁止する」以外にも、「ユーザーに応じて表示を変える」というものもある。そこでログインユーザーの情報(ユーザーネームとメールアドレス)をメイン画面に表示させるよう編集する。

View編集

views.pyファイルを以下のように編集する。

  • requestuser属性にユーザー情報が入っているので取得する。
  • その値をコンテキストに格納し、render()でテンプレートに表示する。
django/login_app/views.py
  @login_required
  def index(request):
-     return render(request, "login_app/index.html")
+     user = request.user
+     params = {"user": user}
+     return render(request, "login_app/index.html", params)

Template編集

index.htmlファイルを以下のように編集する。{{ user.xxxx }}でViewから渡されたユーザー情報を表示させる。

django/login_app/templates/login_app/index.html
  <!DOCTYPE html>
  <html lang="ja">
    <head>
      <meta charset="UTF-8" />
      <title>メイン画面</title>
    </head>
    <body>
      <h1>Hello Login!</h1>
+     <ul>
+       <li>username: {{ user.username }}</li>
+       <li>email: {{ user.email }}</li>
+     </ul>
    </body>
  </html>

動作確認

http://127.0.0.1:8000/login_app/loginでログインすると、メイン画面にusernameemailが表示される。

(TODO: メイン画面)

GitHub

TODO: コード載せる

感想

@login_requiredと書くだけで未ログインユーザーのアクセスを禁止できたり、AuthenticationFormを継承するだけでログインフォームをお手軽に実装できたりと、「え!それだけで!?」と思った。

次はクラスベースビューのログインについて調べたい。