Python、Django ログイン、チュートリアル(パスワード強化版)
今回は、Django のログイン、チュートリアルを書こうと思います。
パスワードを強化した、ログイン機能のみに、ほぼ特化したチュートリアルを実装します。
ややこしくなるかもですが、なるたけシンプルにわかりやすくをモットーに書こうと思うので、よろしくお願いしまっす!
ちょぃ長めになりますが、いきまっす♪
環境
Windows10、VSCode、Django(v5.1.7)
フォルダ構成
(注:最終、作成・変更したファイルのみです)
login
├ settings.py
├ urls.py
├ templates(以下、省略)
└ user
├ forms.py
├ urls.py
└ views.py
今回やること
- 初期設定
- サインアップ・ログイン機能の実装
- ログアウト機能の実装
- 今回のプロジェクト(コード)
1. 初期設定
通常の初期設定を行い、今回はプロジェクト全体でテンプレート・フォルダを使う仕様にします。
Django デフォルトでは、アプリフォルダの中に templates を作成すれば、設定なしで認識してくれますが、プロジェクト全体でテンプレート・フォルダを使う場合は、ディレクトリの設定が必要です。
その場合、設定したディレクトリが優先されるようです。
まず、プロジェクトを作成します。
django-admin startproject login
アプリを作ります。
cd login
python manage.py startapp user
初期設定、パスワード強化設定をします。
PASSWORD_HASHERS を追加します。
passwords: https://docs.djangoproject.com/ja/5.2/topics/auth/passwords/
BCrypt を使うので bcrypt パッケージをインストールします。
pip install bcrypt
import os
...
BASE_DIR = Path(__file__).resolve().parent.parent
TEMPLATE_DIR = os.path.join(BASE_DIR, 'templates') # 追加
...
INSTALLED_APPS = [
...
'user', # 追加
]
...
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [TEMPLATE_DIR], # 追加
...
# 追加
PASSWORD_HASHERS = [
"django.contrib.auth.hashers.BCryptSHA256PasswordHasher",
"django.contrib.auth.hashers.PBKDF2PasswordHasher",
"django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher",
"django.contrib.auth.hashers.Argon2PasswordHasher",
"django.contrib.auth.hashers.ScryptPasswordHasher",
]
AUTH_PASSWORD_VALIDATORS = [
...
LANGUAGE_CODE = 'ja' # 変更
TIME_ZONE = 'Asia/Tokyo' # 変更
...
STATIC_URL = '/static/' # / スラッシュ追加(参考まで)
マイグレートします。
python manage.py migrate
URL パターンを設定していきます。
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('user/', include('user.urls')),
]
app_name、name を設定すると、HTML ファイルで、<a href="{% url 'home:index' %}">ホーム</a> みたいな形式でリンクをはったりできます。(リダイレクト時にも使えるので後に実装します)
from django.urls import path
from . import views
app_name = 'user'
urlpatterns = [
path('', views.home, name='home'),
]
user/views.py に home 関数を作ります。
from django.shortcuts import render
def home(request):
return render(request, 'user/home.html', context={})
login/templates フォルダを作成します。(login はプロジェクト・フォルダ)
templates/user/home.html を作成します。(動作確認用なので、後で内容変更します)
<h1>ホーム</h1>
動作確認します。
python manage.py runserver 8080
http://127.0.0.1:8080/user
にアクセス。(http://localhost:8080/user
も可)
2. サインアップ・ログイン機能の実装
URL パターンを設定します。
...
from . import views
...
urlpatterns = [
...
path('signup/', views.signup_func, name='signup'),
path('login/', views.login_func, name='login'),
...
入力フォーム用のファイルを作ります。
フォーム表示用の調整を行い、clean メソッドで、パスワード認証の強化を実装します。
validate_password: https://docs.djangoproject.com/ja/5.2/topics/auth/passwords/#django.contrib.auth.password_validation.validate_password
from django import forms
from django.contrib.auth.models import User
from django.contrib.auth.password_validation import validate_password
from django.core.exceptions import ValidationError
class SignupForm(forms.ModelForm):
class Meta():
model = User
fields = ('username', 'email', 'password')
labels = {
'username': 'ユーザー名',
'email': 'メールアドレス',
'password': 'パスワード',
}
widgets = {'password': forms.PasswordInput()}
reconfirmation_password = forms.CharField(
label='パスワード再確認',
widget=forms.PasswordInput()
)
def clean(self):
cleaned_data = super().clean()
password = cleaned_data['password']
reconfirmation_password = cleaned_data['reconfirmation_password']
if password != reconfirmation_password:
self.add_error('password', 'パスワードが一致しません')
try:
validate_password(password, self.instance) # instance: 入力ユーザー情報
except ValidationError as e:
self.add_error('password', e)
return cleaned_data
class LoginForm(forms.Form):
username = forms.CharField(label="ユーザー名")
password = forms.CharField(label="パスワード", widget=forms.PasswordInput())
HTML に渡すビューを作成します。
表示内容を設定、入力内容を検証し、遷移先を設定します。
render メソッドは、リクエストを受け、遷移先を設定し、遷移先で表示できる変数?を辞書 context で指定します。
render: https://docs.djangoproject.com/ja/5.2/topics/http/shortcuts/#render
redirect メソッドは、遷移先のパス・ファイルを指定する感じです。name を使います。
redirect: https://docs.djangoproject.com/ja/5.2/topics/http/shortcuts/#redirect
また、messages でメッセージを表示するようにしてます。
messages: https://docs.djangoproject.com/ja/5.2/ref/contrib/messages/
from django.contrib import messages
from django.contrib.auth import authenticate, login
from django.shortcuts import render, redirect
from .forms import SignupForm, LoginForm
...
def signup_func(request):
if request.method == 'POST':
signup_form = SignupForm(request.POST)
if signup_form.is_valid(): # True or False
user = signup_form.save(commit=False) # commit=False の場合のみインスタンスを返す
password = signup_form.cleaned_data['password']
user.set_password(password) # パスワードのハッシュ化
user.save()
messages.success(request, 'サインアップに成功しました')
login(request, user)
return redirect('user:index')
else:
messages.error(request, 'サインアップに失敗しました')
return redirect('user:signup')
else:
signup_form = SignupForm()
return render(request, 'user/signup.html', context={'form': signup_form})
def login_func(request):
if request.method == 'POST':
login_form = LoginForm(request.POST)
if login_form.is_valid():
username = login_form.cleaned_data['username']
password = login_form.cleaned_data['password']
user = authenticate(username=username, password=password)
if user:
login(request, user)
messages.success(request, 'ログインしました')
return redirect('user:home')
else:
messages.error(request, 'ログインに失敗しました')
return redirect('user:login')
else:
messages.error(request, 'ログインに失敗しました')
return redirect('user:login')
else:
login_form = LoginForm()
return render(request, 'user/login.html', context={'form': login_form})
表示する HTML ファイルを作成していきます。
Django の便利な Django テンプレート言語(DTL)を、なるたけ使っていきまっす!
DTL: https://docs.djangoproject.com/ja/5.2/ref/templates/language/
DTL では、{{ }} や {% %} といった表示が使われますが、{{ }} は変数・値、{% %} は制御文などといった感じです。
フォーム入力や、メッセージ表示は、使い回しできるので、スニペットとして作成していきます。
templates フォルダ内に、snippets フォルダを作成します。
ここでの form は、views.py で設定した context 辞書のキーになります。
HTML と CSS を使って、少し表示を調整してます。
<table>
{% for item in form %}
<tr>
<th style="text-align: left;">{{ item.label }}: </th>
<td>{{ item }}</td>
</tr>
{% endfor %}
</table>
{% if form.errors %}
{{ form.errors }}
{% endif %}
スニペットを使って、サインアップページを作成します。
{% csrf_token %} は、クロスサイト・リクエスト・フォージェリ (CSRF) 対策になります。
CSRF 対策: https://docs.djangoproject.com/ja/5.2/ref/csrf/
メッセージ表示用のスニペットも同時に作成します。
views.py で使った messages の表示内容は、messages で取得できます。
{% if messages %}
{% for message in messages %}
{{ message }}
{% endfor %}
{% endif %}
{% include 'snippets/message.html' %}
<form method="POST">
{% csrf_token %}
{% include 'snippets/form_input.html' %}
<br>
<input type="submit" value="サインアップ">
</form>
つづいて、ログインページを作成します。
{% include 'snippets/message.html' %}
<form method="POST">
{% csrf_token %}
{% include 'snippets/form_input.html' %}
<br>
<input type="submit" value="ログイン">
</form>
そして、home.html も書き換えます。
{% if user.is_authenticated %} は、ログイン中かどうかを確認しています。
リンクは、name を使ってはってます。(href="/user/signup" でも可能です)
{% include 'snippets/message.html' %}
{% if user.is_authenticated %}
<h1>{{ user.username }}:ホーム</h1>
{% else %}
サインアップまたはログインしてください<br>
<a href="{% url 'user:signup' %}">サインアップ</a><br>
<a href="{% url 'user:login' %}">ログイン</a>
{% endif %}
これで、サインアップとログインができるようになりました。
サーバーを立ち上げて、サインアップできるかを確認します。(ログアウト機能がないので、ログインは後に確認します。コマンド・プロンプトで、Ctrl + C で停止できます)
python manage.py runserver 8080
http://127.0.0.1:8080/user/signup/
にアクセスし、サインアップします。
サインアップに成功すると、home.html(http://127.0.0.1:8080/user/
) に遷移します。
3. ログアウト機能の実装
URL パターンを設定します。
...
urlpatterns = [
...
path('logout/', views.logout_func, name='logout'),
]
views.py で logout_func を作成します。
...
from django.contrib.auth import authenticate, login, logout
...
def logout_func(request):
logout(request)
messages.success(request, 'ログアウトしました')
return redirect('user:login')
home.html にログアウトのリンクをはります。
...
{% if user.is_authenticated %}
<h1>{{ user.username }}:ホーム</h1>
<a href="{% url 'user:logout' %}">ログアウト</a>
...
動作確認します。
まず、http://127.0.0.1:8080/user/login/
にアクセスしてログインします。
http://127.0.0.1:8080/user/
に遷移します。
ログアウトすると、http://localhost:8080/user/login/
に遷移します。
4. 今回のプロジェクト(コード)
GitHub: https://github.com/Animalyzm/mikoto_project
今回のプロジェクトは、django/login です。
ちょっと、長めになりましたが、以上になります!
ありがとうございましたー♪
Discussion