Python Django 入門トップページ


ユーザ認証とコメント掲示板の開発

  1. プロジェクトの概要
  2. プロジェクトの作成と初期設定
  3. アプリケーションの作成と有効化
  4. ルートの記述
  5. ビューの定義
  6. HTML のテンプレート化
  7. HTML ファイルの作成
  8. Web ページ雛形の表示確認
  9. トップページの作成
  10. データベースのマイグレーション
  11. ユーザの作成
  12. ユーザ認証機能の実装
  13. セッション情報の確認
  14. ユーザ認証の状態を表示
  15. Navbar を設置
  16. モデルの作成とマイグレーション
  17. テストデータの投入
  18. コメントの一覧表示
  19. ページネーションと更新逆順表示
  20. コメント投稿機能の実装
  21. フラッシュメッセージ
  22. コメント詳細表示ページの実装
  23. 投稿者のユーザ名とメールアドレスの表示
  24. 投稿者であるかどうかの判断
  25. 編集・更新機能の実装
  26. コメント削除機能の実装
  27. Navbar の調整
  28. Profile の表示
  29. Profile の編集と更新
  30. パスワードの変更
  31. 管理サイトの準備
  32. 管理ユーザの登録
  33. 管理サイトへのログインとスタッフの登録
  34. グループの追加と権限の付与
  35. 管理サイトに comments アプリケーションを登録
  36. Comments アプリケーションから管理サイトへ

ユーザ認証とコメント掲示板の開発

Profile の編集と更新

ここではユーザの Profile を編集し更新する機能を追加してみよう.具体的には First Name と Last Name だけの更新機能をを追加しますが,方法はコメントの編集・更新と概ね同じです.なお,username の変更も可能ですが,他のユーザとの重複をチェックしなければならないので注意が必要です.E-mail の場合はメールアドレスが正しいことを実際にメールを送信して検証する必要があるでしょう.また,パスワードの変更は次のステップで行います.

まず,urls.py を編集して URL のルートを定義します.

comments/urls.pyfrom django.urls import path

from . import views

app_name = 'comments'
urlpatterns = [
    path('', views.comments_index, name='index'),
    path('<int:comment_id>', views.comments_show, name='show'),
    path('create/', views.comments_create, name='create'),
    path('<int:comment_id>/update/', views.comments_update, name='update'),
    path('<int:comment_id>/delete/', views.comments_delete, name='delete'),
    path('logout/', views.logout_view, name='logout'),
    path('profile/', views.profile, name='profile'),
    path('profile/<int:user_id>/update/', views.profile_update, name='profile_update'),
]

編集フォームの定義を forms.py に追加します.First Name と Last Name の最大文字数は 20 文字にしていますが,必要に応じて変更してください.

comments/forms.pyfrom django import forms
from .models import Comment
from django.contrib.auth.models import User

class CommentForm(forms.ModelForm):

    class Meta:
        model = Comment
        fields = ('title', 'body')
        widgets = {
            'title': forms.TextInput(attrs={
                'class': 'form-control'
            }),
            'body': forms.TextInput(attrs={
                'class': 'form-control'
            }),
        }
        labels = {
            'title': 'タイトル',
            'body': '本文',
        }
    def clean(self):
        data = super().clean()
        title = data.get('title')
        body = data.get('body')
        if title is None:
            msg = "タイトルが入力されていません"
            self.add_error('title', msg)
        elif len(title) > 10:
            msg = "タイトルの最大文字数は10文字です"
            self.add_error('title', msg)
        if body is None:
            msg = "本文が入力されていません"
            self.add_error('body', msg)
        elif len(body) > 20:
            msg = "本文の最大文字数は20文字です"
            self.add_error('body', msg)

class UserForm(forms.ModelForm):

    class Meta:
        model = User
        fields = ('first_name', 'last_name')
        widgets = {
            'first_name': forms.TextInput(attrs={
                'class': 'form-control'
            }),
            'last_name': forms.TextInput(attrs={
                'class': 'form-control'
            }),
        }
        labels = {
            'first_name': 'First Name',
            'last_name': 'Last Name',
        }
    def clean(self):
        data = super().clean()
        first_name = data.get('first_name')
        last_name = data.get('last_name')
        if first_name is None:
            msg = "First Name が入力されていません"
            self.add_error('first_name', msg)
        elif len(first_name) > 20:
            msg = "First Name の最大文字数は20文字です"
            self.add_error('first_name', msg)
        if last_name is None:
            msg = "Last Name が入力されていません"
            self.add_error('last_name', msg)
        elif len(last_name) > 20:
            msg = "Last Name の最大文字数は20文字です"
            self.add_error('last_name', msg)

次に HTML ファイルを作成します.この中で上で定義した form を読み込むようにします.

comments/templates/profile/profile_form.html{% extends "base.html" %}

{% block title %}
Update Profile
{% endblock %}

{% block content %}
  <h1 class="my-5">Update Profile</h1>
  <form method="POST">
    {% csrf_token %}
    {{ form.as_p }}
    <button type="submit" class="btn btn-primary">プロフィールの変更</button>
  </form>

  <ul>
    <li>
      <a href="{% url 'comments:index' %}">コメント一覧</a>
    </li>
    <li>
      <a href="/">トップページへ</a>
    </li>
  </ul>
{% endblock content %}

views.py に編集と更新機能のコードを記述します.

comments/views.pyfrom django.conf import settings
from django.shortcuts import render
from django.shortcuts import get_object_or_404
from django.contrib.auth import authenticate, login, logout
from django.shortcuts import redirect
from django.urls import reverse

from .models import Comment
from .forms import CommentForm
from .forms import UserForm
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.contrib import messages

# Create your views here.


def profile(request):
    if not request.user.is_authenticated:
        return redirect('%s?next=%s' % (settings.LOGIN_URL, request.path))
    context = {}
    context['user'] = request.user
    return render(request, 'profile/show.html', context)

def profile_update(request, user_id):
    if not request.user.is_authenticated:
        return redirect('%s?next=%s' % (settings.LOGIN_URL, request.path))
    if user_id != request.user.id:
            messages.success(request, '他ユーザのProfileは編集できません')
            return redirect(reverse('comments:index'))
    if request.method == 'POST':
        form = UserForm(request.POST)
        if form.is_valid():
            user = request.user
            user.first_name = form.cleaned_data.get('first_name')
            user.last_name = form.cleaned_data.get('last_name')
            user.save()
            messages.success(request, '更新しました')
            return redirect(reverse('comments:profile'))
        else:
            # エラーメッセージをつけて返す
            context = {}
            context['form'] = form
            return render(request, 'profile/profile_form.html', context)
    else:
        context = {}
        context['user'] = request.user
        context['user_id'] = user_id
        context['form'] = UserForm(
                                initial={
                                    'first_name' : request.user.first_name,
                                    'last_name' : request.user.last_name,
                                }
                            )
        return render(request, 'profile/profile_form.html', context)

Profile ページの show.html ファイルに「名前の変更」のためのリンクを設置します.

comments/templates/profile/show.html{% extends "base.html" %}

{% block title %}
Profile
{% endblock %}

{% block content %}
  <h1 class="my-5">Profile</h1>
  <div class="card mb-3">
    <div class="card-header">
      ID : {{ user.id }}
    </div>
    <div class="card-body">
      <p class="card-text">User Name : {{ user.username }}</p>
      <p class="card-text">First Name : {{ user.first_name }}</p>
      <p class="card-text">Last Name : {{ user.last_name }}</p>
      <p class="card-text">Email : {{ user.email }}</p>
      <p class="card-text">Date Joined : {{ user.date_joined }}</p>
      <p class="card-text">Last Login : {{ user.last_login }}</p>
      <p class="card-text">Is Superuser : {{ user.is_superuser }}</p>
      <p class="card-text">Is Staff : {{ user.is_staff }}</p>
      <p class="card-text">Is active : {{ user.is_active }}</p>
      <p class="card-text">Password : {{ user.password }}</p>
    </div>
    <div class="card-footer">
      <p>
        <a href="{% url 'comments:profile_update' user.id  %}">名前の変更</a>
      </p>
    </div>
  </div>
  <hr>


  <ul>
    <li>
      <a href="{% url 'comments:index' %}">コメント一覧</a>
    </li>
    <li>
      <a href="/">トップページへ</a>
    </li>
  </ul>
{% endblock content %}

名前を変更してみます.

django2022-00114

名前の変更ができました.

django2022-00115

次はパスワードの変更機能を作成しましょう.

目次に戻る