Python | Django | ログインの実装方法

2021年7月22日

公開日:2021/7/22

Pythonには,DjangoというWebアプリケーションフレームワークがある.フレームワークのため,Djangoを利用するとWebアプリを通常よりも短時間で開発することが可能になる.

前記事にて,「Viewを利用した複数のアプリケーション作成」を記した.前記事での設定をそのまま引き継いだ上で,本記事では,「ログインの実装方法」を以下にて記す.

◆実施環境

Python 3.8.8
Django 3.2.3

■ログインの実装方法

“viewproject/settings.py"の"AUTH_PASSWORD_VALIDATORS"に追記し,以下のように編集する.

AUTH_PASSWORD_VALIDATORS = [
  {
    'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
  },
  {
    'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    'OPTIONS': { # 追記箇所(7~9行目)
      'min_length': 6,
    }
  },
  {
    'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
  },
  {
    'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
  },
]

以下URLにアクセスし,"viewproject/settings.py"にどのハッシュ関数を選ぶかを決める.

“https://docs.djangoproject.com/en/3.2/topics/auth/passwords/#included-hashers"

“viewproject/settings.py"の"AUTH_PASSWORD_VALIDATORS"のすぐ上に以下のように追記する.ハッシュ関数は,上から順に利用され,利用できなくなる可能性があるので,複数入力する.今回は7つの関数を入力した.

PASSWORD_HASHERS = [ # 追記箇所(1~9行目)
  'django.contrib.auth.hashers.Argon2PasswordHasher',
  'django.contrib.auth.hashers.BCryptSHA256PasswordHasher',
  'django.contrib.auth.hashers.BCryptPasswordHasher',
  'django.contrib.auth.hashers.PBKDF2PasswordHasher',
  'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',
  'django.contrib.auth.hashers.SHA1PasswordHasher',
  'django.contrib.auth.hashers.MD5PasswordHasher',
]

AUTH_PASSWORD_VALIDATORS = [

コマンドプロンプトを起動し,”conda activate 仮想環境名”を実行し,仮想環境に移行する(詳細はこちら参照).その後,"pip install django[argon2]"を実行する.以下が出力されると成功.

(djangoenv) C:\Users\shiro>pip install django[argon2]

Requirement already satisfied: django[argon2] in c:\users\shiro\anaconda3\envs\djangoenv\lib\site-packages (3.2.3) # 以下出力箇所
Requirement already satisfied: pytz in c:\users\shiro\anaconda3\envs\djangoenv\lib\site-packages (from django[argon2]) (2021.1)
Requirement already satisfied: sqlparse>=0.2.2 in c:\users\shiro\anaconda3\envs\djangoenv\lib\site-packages (from django[argon2]) (0.4.1)
Requirement already satisfied: asgiref<4,>=3.3.2 in c:\users\shiro\anaconda3\envs\djangoenv\lib\site-packages (from django[argon2]) (3.3.4)
Collecting argon2-cffi>=19.1.0
Downloading argon2_cffi-20.1.0-cp38-cp38-win_amd64.whl (42 kB)
|████████████████████████████████| 42 kB 340 kB/s
Collecting cffi>=1.0.0
Downloading cffi-1.14.6-cp38-cp38-win_amd64.whl (179 kB)
|████████████████████████████████| 179 kB 6.4 MB/s
Collecting six
Downloading six-1.16.0-py2.py3-none-any.whl (11 kB)
Collecting pycparser
Using cached pycparser-2.20-py2.py3-none-any.whl (112 kB)
Installing collected packages: pycparser, six, cffi, argon2-cffi
Successfully installed argon2-cffi-20.1.0 cffi-1.14.6 pycparser-2.20 six-1.16.0

“user/models.py"に追記し,以下編集をする.

from django.db import models
from django.contrib.auth.models import User
class Profile(models.Model): 
  user = models.OneToOneField(User, on_delete=models.CASCADE) 
  website = models.URLField(blank=True) 
  picture = models.FileField(upload_to='user/', blank=True) 
  def __str__(self): 
    return self.user.username

“user"フォルダに"forms.py"を作成する.プロジェクトの構成は以下のようになる.

“user/forms.py"を以下のように編集する.

from django import forms 
from django.contrib.auth.models import User
from user.models import Profile

class UserForm(forms.ModelForm): 
  username = forms.CharField(label='名前') 
  age = forms.IntegerField(label='年齢') 
  email = forms.EmailField(label='メールアドレス') 
  password = forms.CharField(label='パスワード', widget=forms.PasswordInput()) 
  class Meta(): 
    model = User
    fields = ('username', 'age', 'email', 'password')

class ProfileForm(forms.ModelForm): 
  website = forms.URLField(label='ホームページ') 
  picture = forms.FileField(label='写真') 

  class Meta(): 
    model = Profile 
    fields = ('website', 'picture')

“user/views.py"に追記し,以下のように編集する.

from django.shortcuts import render
from user.forms import UserForm, ProfileForm # 追記箇所

def user_list(request):
  return render(request, 'user/user_list.html')

def index(request): # 以下追記箇所
  return render(request, 'user/index.html')

def register(request): 
  user_form = UserForm(request.POST or None) 
  profile_form = ProfileForm(request.POST or None, request.FILES or None) 
  if user_form.is_valid() and profile_form.is_valid(): 
    user = user_form.save() 
    user.set_password(user.password) 
    user.save() 
    profile = profile_form.save(commit=False) 
    profile.user = user
    profile.save() 
  return render(request, 'user/registration.html', context={ 
    'user_form':user_form, 
    'profile_form':profile_form,
  })

“user/urls.py"に追記し,以下のように編集する.

from django.urls import path
from . import views

app_name = 'user'

urlpatterns = [
  path('user_list/', views.user_list, name='user_list'), 
  path('index/', views.index, name='index'), # 追記箇所(8~9行目)
  path('register/', views.register, name='register'),
]

“user/forms.py"にて写真をアップロードするので,"viewproject/settings.py"に以下コードを追記する.

STTICFILES_DIRS = [
  STATIC_DIR,
]

MEDIA_ROOT = os.path.join(BASE_DIR, 'media') # 以下追記箇所
MEDIA_URL = '/media/'

“viewproject"に"media"フォルダを作成する.プロジェクトの構成は以下になる.

“viewproject/templates/user"に以下のように"BASE.html","index.html","registration.html"を作成する.

“BASE.html"を以下のように編集する.

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<nav>
<a href="{% url 'user:index' %}">ホーム</a>
<a href="{% url 'admin:index' %}">管理画面</a>
<a href="{% url 'user:register' %}">登録画面</a>
</nav>
<div>
{% block content %}
{% endblock %}
</div>
</body>
</html>

“index.html"には以下のように編集する.

{% extends "user/base.html" %}
{% block content %}
<h2>ログインページ</h2>
{% endblock %}

“registration.html"は以下のように編集する.

{% extends "user/base.html" %}
{% block content %}
<h2>登録画面</h2>
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{ user_form.as_p }}
{{ profile_form.as_p }}
<input type="submit" value="登録">
</form>
{% endblock %}

“viewproject/settings.py"の以下箇所を編集する.

DEBUG = True # 変更箇所

ALLOWED_HOSTS = ['127.0.0.1']

ターミナルを開き,”conda activate 仮想環境名”を実行し,仮想環境に移行する(移行方法の詳細はこちら).その後,"cd viewproject"を実行することによって,”viewproject”のディレクトリに移動し,”python manage.py makemigrations アプリ名”を実行する.アプリ名は,"user"とした.以下が出力される.

(djangoenv) C:\Users\shiro\Desktop\210517_python development\myviewtest\viewproject
>python manage.py makemigrations user

Migrations for 'user': # 以下出力箇所
  user\migrations\0001_initial.py
    - Create model Profile

その後,"python manage.py migrate user"を実行する.以下が出力される.

(djangoenv) C:\Users\shiro\Desktop\210517_python development\myviewtest\viewproject
>python manage.py migrate user

Operations to perform: # 以下出力箇所
  Apply all migrations: user
Running migrations:
  Applying user.0001_initial... OK

SQLiteをインストールし(詳細はこちら参照),"SQLITE EXPLORER"を開くと,"user_profile"を以下赤枠にて確認できる.

ブラウザで画面を立ち上げるため,ターミナルにて,"python manage.py runserver"を実行する.

(djangoenv) C:\Users\shiro\Desktop\210517_python 
development\myviewtest\viewproject>python manage.py runserver

System check identified no issues (0 silenced). # 以下出力箇所
July 21, 2021 - 16:31:59
Django version 3.2.3, using settings 'viewproject.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CTRL-BREAK.

上記の"http://127.0.0.1:8000/"をクリックすると,ブラウザにて以下画面が表示される.

“http://127.0.0.1:8000/user/index"を入力すると以下ページに遷移する.

上記ページの"管理画面"をクリックすると以下ページに遷移する.

上記ページの"登録画面"をクリックすると以下ページに遷移する.

以下のように記述し,"登録"をクリックする.

ターミナルには以下のように出力される.

[21/Jul/2021 17:00:47] 
"POST /user/register/ HTTP/1.1" 200 1243

“SQLITE EXPLORER"の"auth_user"を右クリックし,"Show Table"をクリックすると以下が確認できる.

上記"管理画面"にログインするには,"auth_user"の"is_staff"が1である必要がある."is_staff=1″を作成するには,ターミナルにて,”conda activate 仮想環境名”を実行し,仮想環境に移行する.その後,"viewproject"のディレクトリにて,"python manage.py createsuperuser"を実行する.以下のように出力されるので,Usernameなどを入力する.今回,"highest"という名のuserを登録した."Superuser created successfully."が出力されると登録成功となる.

(djangoenv) C:\Users\shiro\Desktop\210517_python development\myviewtest\viewproject
>python manage.py createsuperuser

Username: highest # 以下出力箇所
Email address: highest@gmail.com
Password: 
Password (again):
Superuser created successfully.

“SQLITE EXPLORER"の"auth_user"を右クリックし,"Show Table"をクリックすると,追加した内容が以下にて確認できる.

この情報を用いて,上記ページの"管理画面"にログインすると以下のように管理画面の中に遷移する.

上記の"Users"をクリックすると,以下画面に遷移する.先ほど登録した"highest"も確認できる.

“Profile"の内容を管理画面で表示するには,"viewproject/user/admin.py"を以下のように編集する.

from django.contrib import admin
from user.models import Profile

admin.site.register(Profile)

上記を保存し,"管理画面"にログインすると以下のように"Profile"が追加された.

“user/forms.py"に追記し,以下のように編集する.

from django import forms 
from django.contrib.auth.models import User
from user.models import Profile

class UserForm(forms.ModelForm): 
  username = forms.CharField(label='名前') 
  age = forms.IntegerField(label='年齢') 
  email = forms.EmailField(label='メールアドレス') 
  password = forms.CharField(label='パスワード', widget=forms.PasswordInput()) 
  class Meta(): 
    model = User
    fields = ('username', 'age', 'email', 'password')

class ProfileForm(forms.ModelForm): 
  website = forms.URLField(label='ホームページ') 
  picture = forms.FileField(label='写真') 

  class Meta(): 
    model = Profile 
    fields = ('website', 'picture')

class LoginForm(forms.Form): # 以下追記箇所
  username = forms.CharField(label='名前', max_length=50)
  password = forms.CharField(label='パスワード', widget=forms.PasswordInput())
  confirm_password = forms.CharField(label='パスワード再入力', widget=forms.PasswordInput())

  def clean(self):
    cleaned_data =super().clean()
    password = cleaned_data['password']
    confirm_password = cleaned_data['confirm_password']
    if password != confirm_password:
      raise forms.ValidationError('パスワードが一致しません')

“user/views.py"に追記し,以下のように編集する.

from django.shortcuts import render, redirect # 以下修正箇所(1~5行目)
from django.http import HttpResponse
from user.forms import UserForm, ProfileForm, LoginForm
from django.contrib.auth import authenticate, login, logout
from django.contrib.auth.decorators import login_required

def user_list(request):
  return render(request, 'user/user_list.html')

def index(request):
  return render(request, 'user/index.html')

def register(request): 
  user_form = UserForm(request.POST or None) 
  profile_form = ProfileForm(request.POST or None, request.FILES or None) 
  if user_form.is_valid() and profile_form.is_valid(): 
    user = user_form.save() 
    user.set_password(user.password) 
    user.save() 
    profile = profile_form.save(commit=False) 
    profile.user = user
    profile.save() 
  return render(request, 'user/registration.html', context={ 
    'user_form':user_form, 
    'profile_form':profile_form,
  })

def user_login(request): # 以下追記箇所
  login_form = LoginForm(request.POST or None)
  if login_form.is_valid():
    username = login_form.cleaned_data.get('username')
    password = login_form.cleaned_data.get('password')
    user = authenticate(username=username, password=password)
    if user:
      if user.is_active:
        login(request, user)
        return redirect('user:index')
      else:
        return HttpResponse('アカウントがアクティブでない')
    else:
      return HttpResponse('ユーザーが存在しません')
  return render(request, 'user/login.html', context={
    'login_form': login_form
  })

@login_required
def user_logout(request):
  logout(request)
  return redirect('user:index')

@login_required
def status(request):
  return HttpResponse('ログインしています')

“user/urls.py"に追記し,以下のように編集する.

from django.urls import path
from . import views

app_name = 'user'

urlpatterns = [
  path('user_list/', views.user_list, name='user_list'), 
  path('index/', views.index, name='index'),
  path('register/', views.register, name='register'),
  path('user_login/', views.user_login, name='user_login'), # 追記箇所(10~12行目)
  path('user_logout/', views.user_logout, name='user_logout'),
  path('info/', views.info, name='info'),
]

“viewproject/templates/user"に"login.html"を以下のように作成する.

“login.html"を以下のように編集する.

{% extends "user/base.html" %}
{% block content %}
<h2>ログイン画面</h2>
<form method="POST">
{% csrf_token %}
{{ login_form.as_p }}
<input type="submit" value="ログイン">
</form>
{% endblock %}

“viewproject/templates/user/BASE.html"に追記し,以下のように編集する.

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<nav>
  <a href="{% url 'user:index' %}">ホーム</a>
  <a href="{% url 'admin:index' %}">管理画面</a>
{% if user.is_authenticated %}
  <a href="{% url 'user:user_logout' %}">ログアウト</a>
{% else %}
  <a href="{% url 'user:register' %}">登録画面</a>
  <a href="{% url 'user:user_login' %}">ログイン</a>
{% endif %}
  <a href="{% url 'user:status' %}">status</a>
</nav>
<div>
{% block content %}
{% endblock %}
</div>
</body>
</html>

ターミナルを開き,"python manage.py runserver"を実行する.

(djangoenv) C:\Users\shiro\Desktop\210517_python development\myviewtest\viewproject
>python manage.py runserver

System check identified no issues (0 silenced).
July 22, 2021 - 01:26:50
Django version 3.2.3, using settings 'viewproject.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CTRL-BREAK.

上記の"http://127.0.0.1:8000/"をクリックするとブラウザにて以下画面が開く

上記の"http://127.0.0.1:8000/user/index"を入力すると,以下画面に遷移する.

上記の"ログイン"をクリックすると以下画面に遷移する.

上記で作成した名前とパスワードを入力し,"ログイン"をクリックすると以下ログインページに遷移する.

“status"をクリックすると,以下ページが表示される.

■参照

https://docs.djangoproject.com/en/3.2/topics/auth/passwords/#included-hashers

https://docs.djangoproject.com/en/3.2/topics/auth/passwords/#-django.contrib.auth.password_validation

以上