Python | Django | 掲示板の作成方法

2021年8月16日

公開日:2021/8/16

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

本記事では,「掲示板の作成方法」を以下8項目にて記す.

  1. ホーム画面の作成
  2. ユーザー登録の作成
  3. ログインの作成
  4. ユーザー編集画面の作成
  5. 掲示板画面の作成
    (1) タイトル作成の実装
    (2) 掲示板一覧ページの実装
    (3) 掲示板編集ページの実装
    (4) 掲示板削除ページの実装
    (5) テキスト作成の実装
  6. 一時保存機能の作成
  7. エラー対応機能の作成
  8. 反省点

◆実施環境

Python 3.8.8
Django 3.2.3

■掲示板の作成方法

  1. ホーム画面の作成

新たにフォルダを開く.VS Codeの"File"をクリックし,"Open Folder"からフォルダを選ぶ.

新たなフォルダ名は,"myboardapptest"とした.

ターミナルを開き,”conda activate 仮想環境名”を実行し,仮想環境に移行する(移行方法の詳細はこちら).その後,プロジェクトを作成する(作成方法はこちら).プロジェクト名は,"boardapptest_project"とした."cd boardapptest_project"でディレクトリを変更し,こちらを参照し,アプリのみ作成する(“boardapptest_project/settings.py"の"INSTALLED_APP"に作成したアプリを追加する作業は,本記事の以下にて記載している).アプリ名は,"boardapptest_app"とした.以下が作成後のプロジェクト構成となる.

“boardapptest_project/settings.py"の"INSTALLED_APPS"に作成したアプリ名"boardapptest_app"を以下のように追記する.また,"LANGUAGE_CODE"や"TIME_ZONE"も希望に応じて変更する.

デフォルトのデータベースはsqlite3だが,データベースをmysqlなどに変更したい場合は,DATABASESを編集する.

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

~省略~

LANGUAGE_CODE = 'en-us' # 日本語を希望の場合(en-us => ja)
TIME_ZONE = 'Asia/Tokyo' # 変更箇所(UTC => Asia/Tokyo)

続けて,"boardapptest_project/settings.py"に以下を追記し,編集する.

from pathlib import Path
import os # 追記箇所

BASE_DIR = Path(__file__).resolve().parent.parent
TEMPLATE_DIR = os.path.join(BASE_DIR, 'templates') # 追記箇所
STATIC_DIR = os.path.join(BASE_DIR, 'static') # 追記箇所

~省略~

TEMPLATES = [
  {
    'BACKEND': 'django.template.backends.django.DjangoTemplates',
    'DIRS': [TEMPLATE_DIR,], # 変更箇所
    'APP_DIRS': True,
    'OPTIONS': {
      'context_processors': [
        'django.template.context_processors.debug',
        'django.template.context_processors.request',
        'django.contrib.auth.context_processors.auth',
        'django.contrib.messages.context_processors.messages',
      ],
    },
  },
]

~省略~

STATIC_URL = '/static/'
STATIC_FILES_DIRS = ( # 以下追記箇所
  STATIC_DIR,
)

MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_URL = '/media/'

編集後,プロジェクトフォルダ"boardapptest_project"に3つのフォルダ(“media", “static", “templates")を以下のように作成する.

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

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

“boardapptest_project/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 = [

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

from django.db import models
from django.contrib.auth.models import (
  AbstractBaseUser,
  PermissionsMixin,
)

class User(AbstractBaseUser, PermissionsMixin):
  username = models.CharField(max_length=100)
  email = models.EmailField(max_length=100, unique=True)
  date_of_birth = models.DateField()
  is_active = models.BooleanField(default=False)
  is_staff = models.BooleanField(default=False)
  picture = models.FileField(null=True, upload_to='picture/')

  USERNAME_FIELD = 'email'
  REQUIRED_FIELDS = ['username']

  class Meta:
    db_table = 'user'

“boardapptest_project/settings.py"の"MEDIA_URL"の下に以下コードを追記する.

MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_URL = '/media/'

AUTH_USER_MODEL = 'boardapptest_app.User' # 追記箇所

ターミナルを開き,”conda activate 仮想環境名”を実行し,仮想環境に移行し,"boardapptest_project"のディレクトリにて,"python manage.py makemigrations boardapptest_app"を実行する.実行後,以下が出力される.

(djangoenv) C:\Users\shiro\Desktop\210517_python development\myboardapptest\boardapptest_project
>python manage.py makemigrations boardapptest_app

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

その後,"python manage.py migrate"を実行する.

(djangoenv) C:\Users\shiro\Desktop\210517_python development\myboardapptest\boardapptest_project
>python manage.py migrate

Operations to perform:
Apply all migrations: admin, auth, boardapptest_app, contenttypes, sessions
Running migrations:
Applying contenttypes.0001_initial... OK
Applying contenttypes.0002_remove_content_type_name... OK
Applying auth.0001_initial... OK
Applying auth.0002_alter_permission_name_max_length... OK
Applying auth.0003_alter_user_email_max_length... OK
Applying auth.0004_alter_user_username_opts... OK
Applying auth.0005_alter_user_last_login_null... OK
Applying auth.0006_require_contenttypes_0002... OK
Applying auth.0007_alter_validators_add_error_messages... OK
Applying auth.0008_alter_user_username_max_length... OK
Applying auth.0009_alter_user_last_name_max_length... OK
Applying auth.0010_alter_group_name_max_length... OK
Applying auth.0011_update_proxy_permissions... OK
Applying auth.0012_alter_user_first_name_max_length... OK
Applying boardapptest_app.0001_initial... OK
Applying admin.0001_initial... OK
Applying admin.0002_logentry_remove_auto_add... OK
Applying admin.0003_logentry_add_action_flag_choices... OK
Applying sessions.0001_initial... OK

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

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

from django.shortcuts import render

def home(request):
  return render(
    request, 'boardapptest_app/home.html'
  )

“boardapptest_app"フォルダに"urls.py"を以下のように作成する.

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

from django.urls import path
from . import views

app_name = 'boardapptest_app'

urlpatterns = [
  path('', views.home, name='home'),
]

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

from django.contrib import admin
from django.urls import path, include # 変更箇所

urlpatterns = [
  path('admin/', admin.site.urls),
  path('boardapptest_app/', include('boardapptest_app.urls')), # 追記箇所
]

“boardapptest_project/templates"に"base.html"を以下のように作成する.

“boardapptest_project/templates/base.html"を以下のように編集する.

なお,6行目のbootstrapは以下URLから引用する.

https://getbootstrap.com/docs/5.0/getting-started/introduction/#css

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>Home</title>
<!-- bootstrap -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<a class="navbar-brand" href="{% url 'boardapptest_app:home' %}">Home</a>
</nav>
{% block content %}{% endblock %}
</body>
</html>

“boardapptest_project/templates"にフォルダ"boardapptest_app"を作成し,その中に"home.html"を以下のように作成する.

“boardapptest_project/templates/boardapptest_app/home.html"を以下のように編集する.

{% extends "base.html" %}
{% block content %}
Hello World!<br>
This is home page.
{% endblock %}

ターミナルを開き,仮想環境に移行し,"boardapptest_project"のディレクトリにて,"python manage.py runserver"を実行する.以下が出力される.

(djangoenv) C:\Users\shiro\Desktop\210517_python development\myboardapptest\boardapptest_project
>python manage.py runserver
Watching for file changes with StatReloader
Performing system checks...

System check identified no issues (0 silenced). # 以下出力箇所
August 03, 2021 - 18:14:18
Django version 3.2.3, using settings 'boardapptest_project.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/boardapptest_app"を入力すると以下画面に遷移する.

  1. ユーザー登録の作成

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

from django.urls import path
from . import views

app_name = 'boardapptest_app'

urlpatterns = [
  path('', views.home, name='home'),
  path('registration', views.registration, name='registration'), # 追記箇所
]

“boardapptest_app"に以下のように"forms.py"を作成する.

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

from django import forms
from .models import User
from django.contrib.auth.password_validation import validate_password

class RegistrationForm(forms.ModelForm):
  username = forms.CharField(label='user name')
  date_of_birth = forms.DateField(label='birth day')
  email = forms.EmailField(label='email')
  password = forms.CharField(label='password', widget=forms.PasswordInput())
  reenter_password = forms.CharField(label='re-enter password', widget=forms.PasswordInput())

  class Meta():
    model = User
    fields = ('username', 'date_of_birth', 'email', 'password')

  def clean(self):
    cleaned_data = super().clean()
    password = cleaned_data['password']
    reenter_password = cleaned_data['reenter_password']
    if password != reenter_password:
      raise forms.ValidationError('Password is different. Please try again')

  def save(self, commit=False):
    r_user = super().save(commit=False)
    validate_password(self.cleaned_data['password'], r_user)
    r_user.set_password(self.cleaned_data['password'])
    r_user.save()
    return r_user

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

from django.shortcuts import render
from . import forms
from django.core.exceptions import ValidationError

def home(request):
  return render(
    request, 'boardapptest_app/home.html'
  )

def registration(request):
  registration_form = forms.RegistrationForm(request.POST or None)
  if registration_form.is_valid():
    try:
      registration_form.save()
    except ValidationError as e:
      registration_form.add_error('password', e) 
  return render(
    request, 'boardapptest_app/registration.html', context={
      'registration_form':registration_form,
    }
  )

上記の14行目の"registration_form.save()"の処理がされる場合,"redirect"を実装する.処理後は"home"に遷移するように実装する.そのため,"boardapptest_app/views.py"を以下のように編集する.

from django.shortcuts import render, redirect # 変更箇所
from . import forms
from django.core.exceptions import ValidationError

def home(request):
  return render(
    request, 'boardapptest_app/home.html'
  )

def registration(request):
  registration_form = forms.RegistrationForm(request.POST or None)
  if registration_form.is_valid():
    try:
      registration_form.save()
      return redirect('boardapptest_app:home') # 追記箇所
    except ValidationError as e:
      registration_form.add_error('password', e) 
  return render(
    request, 'boardapptest_app/registration.html', context={
      'registration_form':registration_form,
    }
  )

“boardapptest_project/templates/boardapptest_app"に"registration.html"を以下のように作成する.

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

{% extends "base.html" %}
{% block content %}
<form method="POST">
{% csrf_token %}
{{ registration_form.as_p }}
<input type = "submit" value="user registration">
</form>
{% endblock %}

ユーザーの認証完了によって遷移するよう,"boardapptest_project/templates/base.html"に追記し,以下のように編集する.

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<!-- bootstrap -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-light bg-light">
  <a class="navbar-brand" href="{% url 'boardapptest_app:home' %}">Home</a>
  {% if user.is_authenticated %} # 追記箇所(11~14行目: "r_user"ではなく"user")
  {% else %}
  <a class="navbar-brand" href="{% url 'boardapptest_app:registration' %}">User registration</a>
  {% endif %}
</nav>
{% block content %}{% endblock %}
</body>
</html>

ターミナルを開き,仮想環境に移行し,"boardapptest_project"のディレクトリにて,"python manage.py runserver"を実行する.以下が出力される.

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

System check identified no issues (0 silenced).
August 04, 2021 - 17:06:23
Django version 3.2.3, using settings 'boardapptest_project.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/boardapptest_app"と入力すると以下ページに遷移する.

上記の"User registration"をクリックすると,以下ページに遷移する.

以下にてエラー確認をする.
(*エラー確認画像のURLは正しいURLではないが,訂正していない)

“email"項目にて,"@"を入れないと以下エラーが出現する.

“password"項目にて3文字の短いパスワードを入力すると,以下エラーが出現する.

“password"項目と"re-enter password"の項目の内容が異なると,以下エラーが出現する.

エラーなく各項を記述し,"user registration"をクリックすると,"Home"ページに遷移する.なお,以下のようなエラーがある場合には,こちらを参照すると解消できるかもしれない.

“Couldn’t load 'Argon2PasswordHasher’ algorithm library: No module named 'argon2′"

SQLiteをインストールし(詳細はこちら参照),"SQLITE EXPLORER"を開き,"user"テーブルを右クリックし,"Show Table"をクリックすると以下のように記述内容を確認できる.なお,"is_active"が"0″になっているので,この状態だとログインすることはできない.以降では,現実と同じように,emailが送られ,内容をクリックすると認証されるような処理にする.

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

from django.db import models
from django.contrib.auth.models import (
  AbstractBaseUser,
  PermissionsMixin,
)

class User(AbstractBaseUser, PermissionsMixin):
  username = models.CharField(max_length=100)
  email = models.EmailField(max_length=100, unique=True)  
  date_of_birth = models.DateField()
  is_active = models.BooleanField(default=False)
  is_staff = models.BooleanField(default=False)
  picture = models.FileField(null=True, upload_to='picture/')

  USERNAME_FIELD = 'email'
  REQUIRED_FIELDS = ['username']

  class Meta:
    db_table = 'user'

class UserActiveTokens(models.Model): # 以下追記箇所
  token = models.UUIDField(db_index=True)
  expired_time = models.DateTimeField()
  r_user = models.ForeignKey(
    'User', on_delete=models.CASCADE
  )

  class Meta:
    db_table = 'user_active_tokens'

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

from django.urls import path
from . import views

app_name = 'boardapptest_app'

urlpatterns = [
  path('', views.home, name='home'),
  path('registration', views.registration, name='registration'), 
  path('active_user/<uuid:token>', views.active_user, name='active_user'), # 追記箇所
]

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

from django.shortcuts import render, redirect
from . import forms
from django.core.exceptions import ValidationError

def home(request):
  return render(
    request, 'boardapptest_app/home.html'
  )

def registration(request):
  registration_form = forms.RegistrationForm(request.POST or None)
  if registration_form.is_valid():
    try:
      registration_form.save()
      return redirect('boardapptest_app:home')
    except ValidationError as e:
      registration_form.add_error('password', e) 
  return render(
    request, 'boardapptest_app/registration.html', context={
      'registration_form':registration_form,
    }
  )

def active_user(request, token): # 追記箇所
  pass

ターミナルを開き,”conda activate 仮想環境名”を実行し,仮想環境に移行し,"boardapptest_project"のディレクトリにて,"python manage.py makemigrations boardapptest_app"を実行する.実行後,以下が出力される.

(djangoenv) C:\Users\shiro\Desktop\210517_python development\myboardapptest\boardapptest_project
>python manage.py makemigrations boardapptest_app

Migrations for 'boardapptest_app': # 以下出力箇所
boardapptest_app\migrations\0002_useractivetokens.py
- Create model UserActiveTokens

続けて,"python manage.py migrate"を実行する.実行後,以下が出力される.

(djangoenv) C:\Users\shiro\Desktop\210517_python development\myboardapptest\boardapptest_project
>python manage.py migrate

Operations to perform: # 以下出力箇所
Apply all migrations: admin, auth, boardapptest_app, contenttypes, sessions
Running migrations:
Applying boardapptest_app.0002_useractivetokens... OK

SQLiteをインストールし(詳細はこちら参照),"SQLITE EXPLORER"を開くと,"user_active_tokens"テーブルを以下のように確認できる.

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

from django.db import models
from django.contrib.auth.models import (
  AbstractBaseUser,
  PermissionsMixin,
)
from django.db.models.signals import post_save # 追記箇所(6~9行目)
from django.dispatch import receiver
from uuid import uuid4
from datetime import datetime, timedelta

class User(AbstractBaseUser, PermissionsMixin):
  username = models.CharField(max_length=100)
  email = models.EmailField(max_length=100, unique=True)
  date_of_birth = models.DateField()
  is_active = models.BooleanField(default=False)
  is_staff = models.BooleanField(default=False)
  picture = models.FileField(null=True, upload_to='picture/')

  USERNAME_FIELD = 'email'
  REQUIRED_FIELDS = ['username']

  class Meta:
    db_table = 'user'

class UserActiveTokens(models.Model):
  token = models.UUIDField(db_index=True)
  expired_time = models.DateTimeField()
  r_user = models.ForeignKey(
    'User', on_delete=models.CASCADE
  )

  class Meta:
    db_table = 'user_active_tokens'

@receiver(post_save, sender=User) # 以下追記箇所
def publish_token(sender, instance, **kwargs):
  print(datetime.now() + timedelta(hours=5))
  user_active_token = UserActiveTokens.objects.create(
    r_user=instance, 
    token=str(uuid4()),
    expired_time=datetime.now() + timedelta(hours=5)
  )
  # メールでURLを送る
  print(f'http://127.0.0.1:8000/boardapptest_app/active_user/{user_active_token.token}')

ターミナルを開き,仮想環境のディレクトリ"boardapptest_project"にて,"python manage.py runserver"を実行する.実行後,以下が出力される.

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

System check identified no issues (0 silenced). # 以下出力箇所
August 08, 2021 - 15:13:15
Django version 3.2.3, using settings 'boardapptest_project.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/boardapptest_app"を入力すると以下画面に遷移する.

上記の"User registration"をクリックすると以下画面に遷移する.

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

問題がなければ,自動的に以下"Home"に遷移する.

登録後,ターミナルを開くと,トークン付きの"URL"が出力される.

2021-08-08 20:16:45.148378 # トークンの期限
C:\Users\shiro\anaconda3\envs\djangoenv\lib\site-packages\django\db\models\fields\__init__.py:1416: 
RuntimeWarning: DateTimeField UserActiveTokens.
expired_time received a naive datetime (2021-08-08 20:16:45.149374) 
while time zone support is active. 
warnings.warn("DateTimeField %s received a naive datetime (%s)"
http://127.0.0.1:8000/boardapptest_app/active_user/ea804121-c5c1-40d1-9b6d-8d02c55c0e06 # トークン付きURL

“SQLITE EXPLORER"を開き,"user_active_tokens"を右クリックして"Show Table"をクリックすると以下のように"token"や期限日を確認できる.期限日は登録から5時間後に設定したが,以下のように時間が異なる.最下部の参照によると,SQLITE格納の時間はUTC基準になるため,9時間の誤差が発生する.

ターミナルで出力された期限日:2021/8/8 20:16:45
SQLITE格納の期限日:2021/8/8 11:16:45

トークン付きのURLである"http://127.0.0.1:8000/boardapptest_app/active_user/ea804121-c5c1-40d1-9b6d-8d02c55c0e06″をクリックしても,"boardapptest_app/views.py"の"def active_user(request, token):"の詳細を記載していないため,以下画面が出現する.

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

from django.db import models
from django.contrib.auth.models import (
  AbstractBaseUser, 
  PermissionsMixin,
)
from django.db.models.signals import post_save
from django.dispatch import receiver
from uuid import uuid4
from datetime import datetime, timedelta

class User(AbstractBaseUser, PermissionsMixin):
  username = models.CharField(max_length=100)
  email = models.EmailField(max_length=100, unique=True)
  date_of_birth = models.DateField()
  is_active = models.BooleanField(default=False)
  is_staff = models.BooleanField(default=False)
  picture = models.FileField(null=True, upload_to='picture/')
  USERNAME_FIELD = 'email'
  REQUIRED_FIELDS = ['username']

  class Meta:
    db_table = 'user'

class UserActiveTokensManager(models.Manager): # 追記箇所(25~34行目)

  def active_user_using_token(self, token):
    user_active_token = self.filter(
      token=token,
      expired_time__gte=datetime.now()
    ).first()
    r_user = user_active_token.r_user
    r_user.is_active = True
    r_user.save()
class UserActiveTokens(models.Model):
  token = models.UUIDField(db_index=True)
  expired_time = models.DateTimeField()
  r_user = models.ForeignKey(
    'User', on_delete=models.CASCADE
  )

  objects = UserActiveTokensManager() # 追記箇所

  class Meta:
    db_table = 'user_active_tokens'

@receiver(post_save, sender=User)
def publish_token(sender, instance, **kwargs):
  print(datetime.now() + timedelta(hours=5))
  user_active_token = UserActiveTokens.objects.create(
    r_user=instance, 
    token=str(uuid4()), 
    expired_time=datetime.now() + timedelta(hours=5)
  )
  # メールでURLを送る
  print(f'http://127.0.0.1:8000/boardapptest_app/active_user/{user_active_token.token}')

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

from django.shortcuts import render, redirect
from . import forms
from django.core.exceptions import ValidationError # 追記箇所(3~4行目)
from .models import UserActiveTokens
def home(request):
  return render(
    request, 'boardapptest_app/home.html'
  )

def registration(request):
  registration_form = forms.RegistrationForm(request.POST or None)
  if registration_form.is_valid():
    try:
      registration_form.save()
      return redirect('boardapptest_app:home')
    except ValidationError as e:
      registration_form.add_error('password', e) 
  return render(
    request, 'boardapptest_app/registration.html', context={
      'registration_form':registration_form,
    }

def active_user(request, token):
  user_active_token = UserActiveTokens.objects.active_user_using_token(token) # 以下変更箇所
  return render(
    request, 'boardapptest_app/active_user.html'
  )

“boardapptest_app/views.py"に"boardapptest_app/active_user.html"を追記したので,以下のように"boardapptest_project/templates/boardapptest_app"に"active_user.html"を作成する.

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

{% extends "base.html" %}
{% block content %}
User is valid!
{% endblock %}

上記を保存し,取得したトークン付きのURLである"http://127.0.0.1:8000/boardapptest_app/active_user/ea804121-c5c1-40d1-9b6d-8d02c55c0e06″にアクセスすると以下画面に遷移する.以下画面に遷移すると成功である.

“SQLITE EXPLORER"を開き,"user"を右クリックして"Show Table"をクリックすると以下赤枠のように"is_active"が"1″となるのを確認できる.

  1. ログインの作成

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

from django.urls import path
from . import views

app_name = 'boardapptest_app'

urlpatterns = [
  path('', views.home, name='home'),
  path('registration', views.registration, name='registration'),
  path('active_user/<uuid:token>', views.active_user, name='active_user'),
  path('login_page', views.login_page, name='login_page'), # 追記箇所(10~11行目)
  path('logout_page', views.logout_page, name='logout_page'),
]

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

from django.db import models
from django.contrib.auth.models import (
  AbstractBaseUser, 
  PermissionsMixin,
)
from django.db.models.signals import post_save
from django.dispatch import receiver
from uuid import uuid4
from datetime import datetime, timedelta
from django.contrib.auth.models import UserManager # 追記箇所

class User(AbstractBaseUser, PermissionsMixin):
  username = models.CharField(max_length=100)
  email = models.EmailField(max_length=100, unique=True)
  date_of_birth = models.DateField()
  is_active = models.BooleanField(default=False)
  is_staff = models.BooleanField(default=False)
  picture = models.FileField(null=True, upload_to='picture/')

  objects = UserManager() # 追記箇所

  USERNAME_FIELD = 'email'
  REQUIRED_FIELDS = ['username']

  class Meta:
    db_table = 'user'

class UserActiveTokensManager(models.Manager):

  def active_user_using_token(self, token):
    user_active_token = self.filter(
      token=token,
      expired_time__gte=datetime.now()
    ).first()
    r_user = user_active_token.r_user
    r_user.is_active = True
    r_user.save()

class UserActiveTokens(models.Model):

  token = models.UUIDField(db_index=True)
  expired_time = models.DateTimeField()
  r_user = models.ForeignKey(
    'User', on_delete=models.CASCADE
  )

  objects = UserActiveTokensManager()

  class Meta:
    db_table = 'user_active_tokens'

@receiver(post_save, sender=User)
def publish_token(sender, instance, **kwargs):
  print(datetime.now() + timedelta(hours=5))
  user_active_token = UserActiveTokens.objects.create(
    r_user=instance, 
    token=str(uuid4()), 
    expired_time=datetime.now() + timedelta(hours=5)
  )
  # メールでURLを送る
  print(f'http://127.0.0.1:8000/boardapptest_app/active_user/{user_active_token.token}')

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

from django import forms
from .models import User
from django.contrib.auth.password_validation import validate_password

class RegistrationForm(forms.ModelForm):
  username = forms.CharField(label='user name')
  date_of_birth = forms.DateField(label='birth day')
  email = forms.EmailField(label='email')
  password = forms.CharField(label='password', widget=forms.PasswordInput())
  reenter_password = forms.CharField(label='re-enter password', widget=forms.PasswordInput())

  class Meta():
    model = User
    fields = ('username', 'date_of_birth', 'email', 'password')

  def clean(self):
    cleaned_data = super().clean()
    password = cleaned_data['password']
    reenter_password = cleaned_data['reenter_password']
    if password != reenter_password:
      raise forms.ValidationError('Password is different. Please try again')

  def save(self, commit=False):
    r_user = super().save(commit=False)
    validate_password(self.cleaned_data['password'], r_user)
    r_user.set_password(self.cleaned_data['password'])
    r_user.save()
    return r_user

class LoginForm(forms.Form): # 以下追記箇所
  email = forms.CharField(label="email")
  password = forms.CharField(label="password", widget=forms.PasswordInput())

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

from django.shortcuts import render, redirect
from . import forms
from django.core.exceptions import ValidationError
from .models import UserActiveTokens
from django.contrib.auth import authenticate, login, logout # 以下追記箇所(5~7行目)
from django.contrib import messages
from django.contrib.auth.decorators import login_required

def home(request):
  return render(
    request, 'boardapptest_app/home.html'
  )

def registration(request):
  registration_form = forms.RegistrationForm(request.POST or None)
  if registration_form.is_valid():
    try:
      registration_form.save()
      return redirect('boardapptest_app:home')
    except ValidationError as e:
      registration_form.add_error('password', e) 
  return render(
    request, 'boardapptest_app/registration.html', context={
      'registration_form':registration_form,
    }
  )

def active_user(request, token):
  user_active_token = UserActiveTokens.objects.active_user_using_token(token)
  return render(
    request, 'boardapptest_app/active_user.html'
  )

def login_page(request): # 以下追記箇所
  login_form = forms.LoginForm(request.POST or None)
  if login_form.is_valid():
    email = login_form.cleaned_data.get('email')
    password = login_form.cleaned_data.get('password')
    r_user = authenticate(email=email, password=password)
    if r_user:
      if r_user.is_active:
        login(request,r_user)
        messages.success(request, 'You are successfully logged in')
        return redirect('boardapptest_app:home')
      else:
        messages.warning(request, 'user is not valid')
    else:
      messages.warning(request, 'user or password is wrong')
  return render(
    request, 'boardapptest_app/login_page.html', context={
      'login_form':login_form,
    }
  )

@login_required
def logout_page(request):
  logout(request)
  messages.success(request, 'You have been logged out')
  return redirect('boardapptest_app:home')

“boardapptest_app/views.py"に"login_page.html"を記述したので,以下のように"boardapptest_project/templates/boardapptest_app"に"login_page.html"を作成する.

“boardapptest_project/templates/boardapptest_app/login_page.html"を以下のように編集する.

{% extends "base.html" %}
{% block content %}
{% if messages %}
  {% for message in messages %}
    <div>{{ message.message }}</div>
  {% endfor %}
{% endif %}
<form method="POST">
{% csrf_token %}
{{ login_form.as_p }}
<input type="submit" value="login">
</form>
{% endblock %}

“boardapptest_project/templates/boardapptest_app/home.html"を以下のように編集する.

{% extends "base.html" %}
{% block content %}
{% if messages %} # 以下追記箇所(3~7行目)
  {% for message in messages %}
    <div>{{ message.message }}</div>
  {% endfor %}
{% endif %}
Hello World!<br>
This is home page.
{% endblock %}

“boardapptest_project/templates/base.html"を以下のように編集する.11行目は"user.is_authenticated"であることに注意."r_user.is_authenticated"だとエラーになる.

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<!-- bootstrap -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
</head>
<body>
  <nav class="navbar navbar-expand-lg navbar-light bg-light">
    <a class="navbar-brand" href="{% url 'boardapptest_app:home' %}">Home</a>
    {% if user.is_authenticated %}
    <a class="navbar-brand" href="{% url 'boardapptest_app:logout_page' %}">Logout</a> # 追記箇所
    {% else %}
    <a class="navbar-brand" href="{% url 'boardapptest_app:login_page' %}">Login</a> # 追記箇所
    <a class="navbar-brand" href="{% url 'boardapptest_app:registration' %}">User registration</a>
    {% endif %}
  </nav>
{% block content %}{% endblock %}
</body>
</html>

ターミナルを開き,仮想環境のディレクトリ"boardapptest_project"にて,"python manage.py runserver"を実行する.実行後,以下が出力される.

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

System check identified no issues (0 silenced).
August 09, 2021 - 18:54:24
Django version 3.2.3, using settings 'boardapptest_project.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/boardapptest_app"を入力すると,以下画面に遷移する.

上記の"Login"をクリックすると"Login page"に遷移する.

“is_active"が"1″であるユーザー情報を以下のように記述し,"login"をクリックする.

情報に問題がなければ,以下ログイン画面に遷移する.

上記の"Logout"をクリックすると,以下画面に遷移する.

  1. ユーザー編集画面の作成

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

from django.shortcuts import render, redirect
from . import forms
from django.core.exceptions import ValidationError
from .models import UserActiveTokens
from django.contrib.auth import authenticate, login, logout
from django.contrib import messages
from django.contrib.auth.decorators import login_required

def home(request):
  return render(
    request, 'boardapptest_app/home.html'
  )

def registration(request):
  registration_form = forms.RegistrationForm(request.POST or None)
  if registration_form.is_valid():
    try:
      registration_form.save()
      return redirect('boardapptest_app:home')
    except ValidationError as e:
      registration_form.add_error('password', e) 
  return render(
    request, 'boardapptest_app/registration.html', context={
      'registration_form':registration_form,
    }
  )

def active_user(request, token):
  user_active_token = UserActiveTokens.objects.active_user_using_token(token)
  return render(
    request, 'boardapptest_app/active_user.html'
  )

def login_page(request):
  login_form = forms.LoginForm(request.POST or None)
  if login_form.is_valid():
    email = login_form.cleaned_data.get('email')
    password = login_form.cleaned_data.get('password')
    r_user = authenticate(email=email, password=password)
    if r_user:
      if r_user.is_active:
        login(request, r_user)
        messages.success(request, 'You are successfully logged in')
        return redirect('boardapptest_app:home')
      else:
        messages.warning(request, 'user is not valid')
    else:
      messages.warning(request, 'user or password is wrong')
  return render(
    request, 'boardapptest_app/login_page.html', context={
      'login_form':login_form,
    }
  )

@login_required
def logout_page(request):
  logout(request)
  messages.success(request, 'You have been logged out')
  return redirect('boardapptest_app:home')

@login_required # 以下追記箇所
def edit_page(request):
  edit_form = forms.UserEditForm(
    request.POST or None, 
    request.FILES or None, 
    instance = request.user
    )
  if edit_form.is_valid():
    messages.success(request, 'You have been updated')
    edit_form.save()
  return render(request, 'boardapptest_app/edit_page.html', context={
      'edit_form': edit_form,
  })

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

from django import forms
from .models import User
from django.contrib.auth.password_validation import validate_password

class RegistrationForm(forms.ModelForm):
  username = forms.CharField(label='user name')
  date_of_birth = forms.DateField(label='birth day')
  email = forms.EmailField(label='email')
  password = forms.CharField(label='password', widget=forms.PasswordInput())
  reenter_password = forms.CharField(label='re-enter password', widget=forms.PasswordInput())

  class Meta():
    model = User
    fields = ('username', 'date_of_birth', 'email', 'password')

  def clean(self):
    cleaned_data = super().clean()
    password = cleaned_data['password']
    reenter_password = cleaned_data['reenter_password']
    if password != reenter_password:
      raise forms.ValidationError('Password is different. Please try again')

  def save(self, commit=False):
    r_user = super().save(commit=False)
    validate_password(self.cleaned_data['password'], r_user)
    r_user.set_password(self.cleaned_data['password'])
    r_user.save()
    return r_user

class LoginForm(forms.Form):
  email = forms.CharField(label="email")
  password = forms.CharField(label="password", widget=forms.PasswordInput())

class UserEditForm(forms.ModelForm): # 以下追記箇所
  username = forms.CharField(label='user name')
  date_of_birth = forms.DateField(label='birth day')
  email = forms.EmailField(label='email')
  picture = forms.FileField(label='picture', required=False)

  class Meta:
    model = User
    fields = ('username', 'date_of_birth', 'email', 'picture')

“boardapptest_app/views.py"で"boardapptest_app/edit_page.html"を記述したので,"templates/boardapptest_app"に"edit_page.html"を以下のように作成する.

“templates/boardapptest_app/edit_page.html"を以下のように編集する.

{% extends "base.html" %}
{% block content %}
{% if messages %}
  {% for message in messages %}
    <div>{{ message.message }}</div>
  {% endfor %}
{% endif %}
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{ edit_form.as_p }}
<input type="submit" value="update">
</form>
<a href="{% url 'boardapptest_app:change_password' %}">change password</a>
{% endblock %}

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

from django.urls import path
from . import views

app_name = 'boardapptest_app'

urlpatterns = [
    path('', views.home, name='home'),
    path('registration', views.registration, name='registration'),
    path('active_user/', views.active_user, name='active_user'),
    path('login_page', views.login_page, name='login_page'),
    path('logout_page', views.logout_page, name='logout_page'),
    path('edit_page', views.edit_page, name='edit_page'), # 追記箇所(13~14行目)
    path('change_password', views.change_password, name='change_password'),
]

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

from django.shortcuts import render, redirect
from . import forms
from django.core.exceptions import ValidationError
from .models import UserActiveTokens
from django.contrib.auth import authenticate, login, logout
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.contrib.auth import update_session_auth_hash # 追記箇所

def home(request):
  return render(
    request, 'boardapptest_app/home.html'
  )

def registration(request):
  registration_form = forms.RegistrationForm(request.POST or None)
  if registration_form.is_valid():
    try:
      registration_form.save()
      return redirect('boardapptest_app:home')
    except ValidationError as e:
      registration_form.add_error('password', e) 
  return render(
    request, 'boardapptest_app/registration.html', context={
      'registration_form':registration_form,
    }
  )

def active_user(request, token):
  user_active_token = UserActiveTokens.objects.active_user_using_token(token)
  return render(
    request, 'boardapptest_app/active_user.html'
  )

def login_page(request):
  login_form = forms.LoginForm(request.POST or None)
  if login_form.is_valid():
    email = login_form.cleaned_data.get('email')
    password = login_form.cleaned_data.get('password')
    r_user = authenticate(email=email, password=password)
    if r_user:
      if r_user.is_active:
        login(request, r_user)
        messages.success(request, 'You are successfully logged in')
        return redirect('boardapptest_app:home')
      else:
        messages.warning(request, 'user is not valid')
    else:
      messages.warning(request, 'user or password is wrong')
  return render(
    request, 'boardapptest_app/login_page.html', context={
      'login_form':login_form,
    }
  )

@login_required
def logout_page(request):
  logout(request)
  messages.success(request, 'You have been logged out')
  return redirect('boardapptest_app:home')

@login_required
def edit_page(request):
  edit_form = forms.UserEditForm(
    request.POST or None, 
    request.FILES or None, 
    instance = request.user
    )
  if edit_form.is_valid():
    messages.success(request, 'You have been updated')
    edit_form.save()
  return render(request, 'boardapptest_app/edit_page.html', context={
      'edit_form': edit_form,
  })

@login_required # 以下追記箇所
def change_password(request):
  change_password_form = forms.ChangePasswordForm(request.POST or None, instance=request.user) 
  if change_password_form.is_valid():
    try:
      change_password_form.save()
      messages.success(request, 'You have been updated password')
      update_session_auth_hash(request, request.user)
    except ValidationError as e:
      change_password_form.add_error('password', e)
  return render(
    request, 'boardapptest_app/change_password.html', context={
      'change_password_form': change_password_form,
    }
  )

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

from django import forms
from .models import User
from django.contrib.auth.password_validation import validate_password

class RegistrationForm(forms.ModelForm):
  username = forms.CharField(label='user name')
  date_of_birth = forms.DateField(label='birth day')
  email = forms.EmailField(label='email')
  password = forms.CharField(label='password', widget=forms.PasswordInput())
  reenter_password = forms.CharField(label='re-enter password', widget=forms.PasswordInput())

  class Meta():
    model = User
    fields = ('username', 'date_of_birth', 'email', 'password')

  def clean(self):
    cleaned_data = super().clean()
    password = cleaned_data['password']
    reenter_password = cleaned_data['reenter_password']
    if password != reenter_password:
      raise forms.ValidationError('Password is different. Please try again')

  def save(self, commit=False):
    r_user = super().save(commit=False)
    validate_password(self.cleaned_data['password'], r_user)
    r_user.set_password(self.cleaned_data['password'])
    r_user.save()
    return r_user

class LoginForm(forms.Form):
  email = forms.CharField(label="email")
  password = forms.CharField(label="password", widget=forms.PasswordInput())

class UserEditForm(forms.ModelForm):
  username = forms.CharField(label='user name')
  date_of_birth = forms.DateField(label='birth day')
  email = forms.EmailField(label='email')
  picture = forms.FileField(label='picture', required=False)

  class Meta:
    model = User
    fields = ('username', 'date_of_birth', 'email', 'picture')

class ChangePasswordForm(forms.ModelForm): # 以下追記箇所
    
  password = forms.CharField(label='password', widget=forms.PasswordInput())
  reenter_password = forms.CharField(label='re-enter password', widget=forms.PasswordInput())

  class Meta():
    model = User
    fields = ('password',)

  def clean(self):
    cleaned_data = super().clean()
    password = cleaned_data['password']
    reenter_password = cleaned_data['reenter_password']
    if password != reenter_password:
      raise forms.ValidationError('Password is different. Please try again')

  def save(self, commit=False):
    r_user = super().save(commit=False)
    validate_password(self.cleaned_data['password'], r_user)
    r_user.set_password(self.cleaned_data['password'])
    r_user.save()
    return r_user

“boardapptest_app/views.py"に"boardapptest_app/change_password.html"を記述したので,以下のように"boardapptest_project/templates/boardapptest_app"に"change_password.html"を作成する.

“boardapptest_project/templates/boardapptest_app/change_password.html"を以下のように編集する.

{% extends "base.html" %}
{% block content %}
{% if messages %}
  {% for message in messages %}
    <div>{{ message.message }}</div>
  {% endfor %}
{% endif %}
<form method="POST">
{% csrf_token %}
{{ change_password_form.as_p }}
<input type="submit" value="update password">
</form>
{% endblock %}

“boardapptest_project/templates/boardapptest_app/base.html"に追記し,以下のように編集する.

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<!-- bootstrap -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
</head>
<body>
  <nav class="navbar navbar-expand-lg navbar-light bg-light">
    <a class="navbar-brand" href="{% url 'boardapptest_app:home' %}">Home</a>
    {% if user.is_authenticated %}
    <a class="navbar-brand" href="{% url 'boardapptest_app:logout_page' %}">Logout</a>
    <a class="navbar-brand" href="{% url 'boardapptest_app:edit_page' %}">Edit Info</a> # 追記箇所
    {% else %}
    <a class="navbar-brand" href="{% url 'boardapptest_app:login_page' %}">Login</a>
    <a class="navbar-brand" href="{% url 'boardapptest_app:registration' %}">User registration</a>
    {% endif %}
  </nav>
{% block content %}{% endblock %}
</body>
</html>

ターミナルを開き,仮想環境のディレクトリ"boardapptest_project"にて,"python manage.py runserver"を実行する.実行後,以下が出力される.

(djangoenv) C:\Users\shiro\Desktop\210517_python development\myboardapptest\boardapptest_project>python manage.py runserver
Watching for file changes with StatReloader
Performing system checks...

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

ターミナルの"http://127.0.0.1:8000/"をクリックするとブラウザが立ち上がる.ブラウザのURLに"http://127.0.0.1:8000/boardapptest_app"を入力すると以下画面に遷移する.

上記の"Login"をクリックすると以下画面に遷移する.登録したemailとpasswordを記述し,"Login"をクリックする.

Loginに成功すると以下画面に遷移する.

上記の"Edit Info"をクリックすると以下画面に遷移する.このページでログイン情報を変更することができる.

“SQLITE EXPLORER"を開き,"user"を右クリックして"Show Table"をクリックすると以下のように上記のログイン情報を確認できる.

“Edit Info"のページを以下のように変更する.ファイルには"ジッパー.jpg"をアップロードする.その後,"update"をクリックする.

“update"をクリックすると以下赤枠のメッセージが出力されると成功となる.

VS Code内の"boardapptest_project/media"にはアップロードした画像が保存されている.

“SQLITE EXPLORER"の"user"テーブルは以下赤枠のように更新された.

“Edit Info"のページの"change password"をクリックすると以下画面に遷移する.以下ページにてパスワード情報を変更することができる.

“password"と"re-enter password"に入力したパスワードが異なると以下赤枠のメッセージが出力される.

“password"と"re-enter password"のパスワードが正しいが,短いと,以下赤枠のメッセージが出力される.

“password"と"re-enter password"のパスワードが正しく,複雑であると,以下赤枠のメッセージが出力される.パスワードの変更が成功となる.

  1. 掲示板画面の作成

(1) タイトル作成の実装

“cd boardapptest_project"でディレクトリを変更し,"boardapptest_app"以外のアプリを作成する.“python manage.py startapp アプリ名"を実行すればアプリが作成される.アプリ名は"board_app"とした.

新たにアプリを作成したので,"boardapptest_project/settings.py"の"INSTALLED_APPS"を以下のように編集する.

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'boardapptest_app',
    'board_app', # 追記箇所
]

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

from django.db import models

class Topics(models.Model):

  title = models.CharField(max_length=100)
  user = models.ForeignKey(
    'boardapptest_app.User', on_delete=models.CASCADE
  )

  class Meta:
    db_table = 'topics'

class Texts(models.Model):

  text = models.CharField(max_length=500)
  user = models.ForeignKey(
    'boardapptest_app.User', on_delete=models.CASCADE
  )
  topic = models.ForeignKey(
    'Topics', on_delete=models.CASCADE
  )

  class Meta:
    db_table = 'texts'

ターミナルを開き,”conda activate 仮想環境名”を実行し,仮想環境に移行し,"boardapptest_project"のディレクトリにて,"python manage.py makemigrations board_app"を実行する.実行後,以下が出力される.

(djangoenv) C:\Users\shiro\Desktop\210517_python development\myboardapptest\boardapptest_project
>python manage.py makemigrations board_app

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

続けて,ターミナルにて,"python manage.py migrate"を実行する.実行後,以下が出力される.

(djangoenv) C:\Users\shiro\Desktop\210517_python development\myboardapptest\boardapptest_project
>python manage.py migrate

Operations to perform: # 以下出力箇所
  Apply all migrations: admin, auth, board_app, boardapptest_app, contenttypes, sessions
Running migrations:
  Applying board_app.0001_initial... OK

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

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
  path('admin/', admin.site.urls),
  path('boardapptest_app/', include('boardapptest_app.urls')),
  path('board_app/', include('board_app.urls')), # 追記箇所
]

“board_app"に"urls.py"を以下のように作成する.

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

from django.urls import path
from . import views

app_name = 'board_app'

urlpatterns = [
  path('create_topic', views.create_topic, name='create_topic')
]

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

from django.shortcuts import render
from . import forms

def create_topic(request):
  create_topic_form = forms.CreateTopicForm(request.POST or None)

“board_app/views.py"で"forms"を利用しているので,"board_app"フォルダに"forms.py"を作成する.

“board_app/forms.py"に"CreateTopicForm"についてのコードを以下のように作成する.

from django import forms
from .models import Topics, Texts

class CreateTopicForm(forms.ModelForm):
  title = forms.CharField(label='Title')

  class Meta:
    model = Topics
    fields = ('title',)

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

from django.shortcuts import render, redirect
from . import forms
from django.contrib import messages

def create_topic(request):
  create_topic_form = forms.CreateTopicForm(request.POST or None)
  if create_topic_form.is_valid():
    create_topic_form.instance.user = request.user
    create_topic_form.save()
    messages.success(request, 'You have been created board')
    return redirect('boardapptest_app:home')
  return render(
    request, 'board_app/create_topic.html', context={
      'create_topic_form': create_topic_form,
    }
  )

“board_app/views.py"で"board_app/create_topic.html"のコードを記述したので,"boardapptest_project/templates"に"board_app"フォルダを作成し,その中に"create_topic.html"を以下のように作成する.

“boardapptest_project/templates/board_app/create_topic.html"を以下のように編集する.

{% extends "base.html" %}
{% block content %}
<h2>page for board creation</h2>
<form method="POST">
{% csrf_token %}
{{ create_topic_form.as_p }}
<input type = "submit" value="board creation">
</form>
{% endblock %}

“create_topic.html"に遷移させるため,"boardapptest_project/templates/base.html"を以下のように編集する.

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<!-- bootstrap -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
</head>
<body>
  <nav class="navbar navbar-expand-lg navbar-light bg-light">
    <a class="navbar-brand" href="{% url 'boardapptest_app:home' %}">Home</a>
    {% if user.is_authenticated %}
    <a class="navbar-brand" href="{% url 'board_app:create_topic' %}">Board creation</a> # 追記箇所
    <a class="navbar-brand" href="{% url 'boardapptest_app:logout_page' %}">Logout</a>
    <a class="navbar-brand" href="{% url 'boardapptest_app:edit_page' %}">Edit Info</a>
    {% else %}
    <a class="navbar-brand" href="{% url 'boardapptest_app:login_page' %}">Login</a>
    <a class="navbar-brand" href="{% url 'boardapptest_app:registration' %}">User registration</a>
    {% endif %}
  </nav>
{% block content %}{% endblock %}
</body>
</html>

ターミナルを開き,仮想環境に移行し,"boardapptest_project"のディレクトリにて,"python manage.py runserver"を実行する.以下が出力される.

(djangoenv) C:\Users\shiro\Desktop\210517_python development\myboardapptest\boardapptest_project
>python manage.py runserver
Watching for file changes with StatReloader
Performing system checks...

System check identified no issues (0 silenced). # 以下出力箇所
August 12, 2021 - 16:39:20
Django version 3.2.3, using settings 'boardapptest_project.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/boardapptest_app"を入力し,以下画面に遷移する.

上記の"Login"をクリックすると以下画面に遷移する.登録したユーザー情報を記述し,ログインをする.

ログインが成功すると,以下のように"Board creation"の項目が作成されているのが確認できる.

上記の"Board creation"をクリックすると,以下画面に遷移する.

“Title"に以下のように"board 1″と記述し,"board creation"をクリックする.

上記にて"board creation"をクリックすると,以下赤枠のメッセージが出現する.

“SQLITE EXPLORER"を開き,"topics"テーブルを右クリックして"Show Table"をクリックする.

以下のように登録した"board 1″をデータベースのテーブルにて確認することができる.

(2) 掲示板一覧ページの実装

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

from django.db import models

class TopicsManager(models.Manager): # 追記箇所(3~6行目)

  def pick_all_topics(self):
    return self.order_by('id').all()

class Topics(models.Model):

  title = models.CharField(max_length=100)
  user = models.ForeignKey(
    'boardapptest_app.User', on_delete=models.CASCADE
  )

  objects = TopicsManager() # 追記箇所

  class Meta:
    db_table = 'topics'

class Texts(models.Model):

  text = models.CharField(max_length=500)
  user = models.ForeignKey(
    'boardapptest_app.User', on_delete=models.CASCADE
  )
  topic = models.ForeignKey(
    'Topics', on_delete=models.CASCADE
  )

  class Meta:
    db_table = 'texts'

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

from django.shortcuts import render, redirect
from . import forms
from django.contrib import messages
from .models import Topics # 追記箇所

def create_topic(request):
  create_topic_form = forms.CreateTopicForm(request.POST or None)
  if create_topic_form.is_valid():
    create_topic_form.instance.user = request.user
    create_topic_form.save()
    messages.success(request, 'You have been created board')
    return redirect('board_app:list_topics') # 編集箇所
  return render(
    request, 'board_app/create_topic.html', context={
      'create_topic_form': create_topic_form,
    }
  )

def list_topics(request): # 以下追記箇所
  topics = Topics.objects.pick_all_topics()
  return render(
    request, 'board_app/list_topics.html', context={
      'topics': topics
    }
  )

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

from django.urls import path
from . import views

app_name = 'board_app'

urlpatterns = [
  path('create_topic', views.create_topic, name='create_topic'),
  path('list_topics', views.list_topics, name='list_topics'), # 追記箇所
]

“board_app/views.py"で"board_app/list_topics.html"のコードを記述したので,"boardapptest_project/templates/board_app"に"list_topics.html"を以下のように作成する.

“boardapptest_project/templates/board_app/list_topics.html"を以下のように編集する.

{% extends "base.html" %}
{% block content %}
{% if messages %}
  {% for message in messages %}
  <div>{{ message.message }}</div>
  {% endfor %}
{% endif %}
<h2>page for board list</h2>
<p><a class="navbar-brand" href="{% url 'board_app:create_topic' %}">New board creation</a></p>
<table class="table table-striped table-hover">
  <thread>
    <tr>
      <th>No.</th>
      <th>Title</th>
      <th>Author</th>
    </tr>
  </thread>
  <tbody>
  {% for topic in topics %}
    <tr>
      <td>{{ forloop.counter }}</td>
      <td>{{ topic.title }}</td>
      <td>{{ topic.user }}</td>
    </tr>
  {% endfor %}
  </tbody>
</table>
{% endblock %}

“boardapptest_project/templates/base.html"を以下のように編集する.

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<!-- bootstrap -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
</head>
<body>
  <nav class="navbar navbar-expand-lg navbar-light bg-light">
    <a class="navbar-brand" href="{% url 'boardapptest_app:home' %}">Home</a>
    {% if user.is_authenticated %}
    <a class="navbar-brand" href="{% url 'board_app:list_topics' %}">Board list</a> # 変更箇所
    <a class="navbar-brand" href="{% url 'boardapptest_app:logout_page' %}">Logout</a>
    <a class="navbar-brand" href="{% url 'boardapptest_app:edit_page' %}">Edit Info</a>
    {% else %}
    <a class="navbar-brand" href="{% url 'boardapptest_app:login_page' %}">Login</a>
    <a class="navbar-brand" href="{% url 'boardapptest_app:registration' %}">User registration</a>
    {% endif %}
  </nav>
{% block content %}{% endblock %}
</body>
</html>

ターミナルを開き,仮想環境に移行し,"boardapptest_project"のディレクトリにて,"python manage.py runserver"を実行する.以下が出力される.

(djangoenv) C:\Users\shiro\Desktop\210517_python development\myboardapptest\boardapptest_project
>python manage.py runserver
Watching for file changes with StatReloader
Performing system checks...

System check identified no issues (0 silenced). # 以下出力箇所
August 12, 2021 - 19:40:17
Django version 3.2.3, using settings 'boardapptest_project.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/boardapptest_app"を入力すると以下画面に遷移する.

上記の"Board list"をクリックすると,以下画面に遷移する.

上記の"New board creation"をクリックすると,以下画面に遷移する.当該画面で"Title"を作成することができる.

例えば,以下のように"board 3″と記述し,"board creation"ボタンをクリックする.

作成に成功すると,以下赤枠のメッセージとともに"board 3″が一覧に追加される.

(3) 掲示板編集ページの実装

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

from django.urls import path
from . import views

app_name = 'board_app'

urlpatterns = [
  path('create_topic', views.create_topic, name='create_topic'),
  path('list_topics', views.list_topics, name='list_topics'),
  path('edit_topic/<int:id>', views.edit_topic, name='edit_topic'), # 追記箇所
]

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

from django.shortcuts import render, redirect, get_object_or_404 # 変更箇所
from . import forms
from django.contrib import messages
from .models import Topics
from django.http import Http404 # 追記箇所

def create_topic(request):
  create_topic_form = forms.CreateTopicForm(request.POST or None)
  if create_topic_form.is_valid():
    create_topic_form.instance.user = request.user
    create_topic_form.save()
    messages.success(request, 'You have been created board')
    return redirect('board_app:list_topics')
  return render(
    request, 'board_app/create_topic.html', context={
      'create_topic_form': create_topic_form,
    }
  )

def list_topics(request):
  topics = Topics.objects.pick_all_topics()
  return render(
    request, 'board_app/list_topics.html', context={
      'topics': topics
    }
  )

def edit_topic(request, id): # 以下追記箇所
  topic = get_object_or_404(Topics, id=id)
  if topic.user.id != request.user.id:
    raise Http404
  edit_topic_form = forms.CreateTopicForm(request.POST or None, instance=topic)
  if edit_topic_form.is_valid():
    edit_topic_form.save()
    messages.success(request, 'You have been updated board')
    return redirect('board_app:list_topics')
  return render(
    request, 'board_app/edit_topic.html', context={
      'edit_topic_form': edit_topic_form,
      'id': id,
    }
  )

“boardapptest_project/templates/board_app/list_topics.html"を以下のように編集する.

{% extends "base.html" %}
{% block content %}
{% if messages %}
  {% for message in messages %}
  <div>{{ message.message }}</div>
  {% endfor %}
{% endif %}
<h2>page for board list</h2>
<p><a class="navbar-brand" href="{% url 'board_app:create_topic' %}">New board creation</a></p>
<table class="table table-striped table-hover">
  <thread>
    <tr>
      <th>No.</th>
      <th>Title</th>
      <th>Author</th>
    </tr>
  </thread>
  <tbody>
  {% for topic in topics %}
    <tr>
      <td>{{ forloop.counter }}</td>
      <td>{{ topic.title }}</td>
      {% if topic.user.id == user.id %} # 変更・追記箇所(23~27行目)
        <td><a href="{% url 'board_app:edit_topic' id=topic.id %}">{{ topic.user }}</a></td>
      {% else %}
        <td>{{ topic.user }}</td>
      {% endif %}
    </tr>
  {% endfor %}
  </tbody>
</table>
{% endblock %}

“boardapptest_project/templates/board_app"に"edit_topic.html"を以下のように作成する.

“boardapptest_project/templates/board_app/edit_topic.html"を以下のように作成する.

{% extends "base.html" %}
{% block content %}
<h2>page for board edit</h2>
<form method="POST">
{% csrf_token %}
{{ edit_topic_form.as_p }}
<input type = "submit" value="update">
</form>
{% endblock %}

ターミナルを開き,仮想環境に移行し,"boardapptest_project"のディレクトリにて,"python manage.py runserver"を実行する.以下が出力される.

(djangoenv) C:\Users\shiro\Desktop\210517_python development\myboardapptest\boardapptest_project
>python manage.py runserver
Watching for file changes with StatReloader
Performing system checks...

System check identified no issues (0 silenced). # 以下出力箇所
August 13, 2021 - 15:56:11
Django version 3.2.3, using settings 'boardapptest_project.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/boardapptest_app"を入力すると以下画面に遷移する.

上記の"Board list"をクリックすると以下画面に遷移する.現在,"buccellati@email.com“でログイン状態になっているため,赤枠がリンクの仕様となっている.

上記のNo.3の"Author"リンクをクリックすると以下画面に遷移する.

“Title"を以下のように"board 3″から"board 4″に変更し,"update"ボタンをクリックする.

ボタンクリック後,以下画面に遷移し,赤枠のメッセージのように更新が完了し,Titleも"board 3″から"board 4″に変更された.

以下画面にて"Logout"をクリックし,"buccellati@email.com“以外のemailでログインを試みる.

ログアウト後,以下画面に遷移するので,"Login"をクリックする.

以下のように"buccellati@email.com“以外のメールアドレスを記入し,"login"をクリックする.今回は,"shirobana@email.com"でログインをした.

ログインが成功したので,"Board list"をクリックする.

“board list"ページに遷移すると,"shirobana@email.com"でログインをしているので,"buccellati@email.com“はリンクになっていないことが分かる.以下の"New board creation"をクリックする.

以下画面に遷移するので,"board 10″というtitleで作成する.

作成が成功すると以下画面の掲示板リストページに遷移する.赤枠のようにログインしているemailのみがリンクになっている.

(4) 掲示板削除ページの実装

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

from django.shortcuts import render, redirect, get_object_or_404
from . import forms
from django.contrib import messages
from .models import Topics
from django.http import Http404

def create_topic(request):
  create_topic_form = forms.CreateTopicForm(request.POST or None)
  if create_topic_form.is_valid():
    create_topic_form.instance.user = request.user
    create_topic_form.save()
    messages.success(request, 'You have been created board')
    return redirect('board_app:list_topics')
  return render(
    request, 'board_app/create_topic.html', context={
      'create_topic_form': create_topic_form,
    }
  )

def list_topics(request):
  topics = Topics.objects.pick_all_topics()
  return render(
    request, 'board_app/list_topics.html', context={
      'topics': topics
    }
  )

def edit_topic(request, id):
  topic = get_object_or_404(Topics, id=id)
  if topic.user.id != request.user.id:
    raise Http404
  edit_topic_form = forms.CreateTopicForm(request.POST or None, instance=topic)
  if edit_topic_form.is_valid():
    edit_topic_form.save()
    messages.success(request, 'You have been updated board')
    return redirect('board_app:list_topics')
  return render(
    request, 'board_app/edit_topic.html', context={
      'edit_topic_form': edit_topic_form,
      'id': id,
    }
  )

def delete_topic(request, id): # 以下追記箇所
  topic = get_object_or_404(Topics, id=id)
  if topic.user.id != request.user.id:
    raise Http404
  delete_topic_form = forms.DeleteTopicForm(request.POST or None)
  if delete_topic_form.is_valid():
    topic.delete()
    messages.success(request, 'You have been deleted board')
    return redirect('board_app:list_topics')
  return render(
    request, 'board_app/delete_topic.html', context={
      'delete_topic_form': delete_topic_form
    }
  )

“views.py"にて"DeleteTopicForm"を利用したので,"board_app/forms.py"に追記し,以下のように編集する.

from django import forms
from .models import Topics, Texts

class CreateTopicForm(forms.ModelForm):
  title = forms.CharField(label='Title')

  class Meta:
    model = Topics
    fields = ('title',)

class DeleteTopicForm(forms.ModelForm): # 以下追記箇所

  class Meta:
    model = Topics
    fields = []

“views.py"にて"delete_topic.html"を利用したので,"boardapptest_project/templates/board_app"に"delete_topic.html"を以下のように作成する.

“boardapptest_project/templates/board_app/delete_topic.html"を以下のように編集する.

{% extends "base.html" %}
{% block content %}
<h2>page for board delete</h2>
<form method="POST">
{% csrf_token %}
{{ delete_topic_form.as_p }}
<input type = "submit" value="delete">
</form>
{% endblock %}

“edit_topic.html"から"delete_topic.html"に遷移するため,"boardapptest_project/templates/board_app/edit_topic.html"を以下のように編集する.

{% extends "base.html" %}
{% block content %}
<h2>page for board edit</h2>
<form method="POST">
{% csrf_token %}
{{ edit_topic_form.as_p }}
<input type = "submit" value="update">
</form>
<p><a class = "navbar-brand" href="{% url 'board_app:delete_topic' id=id %}">delete</a></p> # 追記箇所
{% endblock %}

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

from django.urls import path
from . import views

app_name = 'board_app'

urlpatterns = [
  path('create_topic', views.create_topic, name='create_topic'),
  path('list_topics', views.list_topics, name='list_topics'),
  path('edit_topic/<int:id>', views.edit_topic, name='edit_topic'),
  path('delete_topic/<int:id>', views.delete_topic, name='delete_topic'), # 追記箇所
]

ターミナルを開き,仮想環境に移行し,"boardapptest_project"のディレクトリにて,"python manage.py runserver"を実行する.以下が出力される.

(djangoenv) C:\Users\shiro\Desktop\210517_python development\myboardapptest\boardapptest_project
>python manage.py runserver
Watching for file changes with StatReloader
Performing system checks...

System check identified no issues (0 silenced). # 以下出力箇所
August 13, 2021 - 17:21:18
Django version 3.2.3, using settings 'boardapptest_project.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/boardapptest_app"を入力すると以下画面に遷移する."Board list"をクリックする.

以下画面に遷移するので,No.4の赤枠のリンクをクリックする.

以下画面の編集ページへ遷移する.最下部の"delete"をクリックする.

以下画面に遷移するので,"delete"ボタンをクリックする.

以下画面に遷移し,No.4が削除された.

(5) テキスト作成の実装

(i) テキストの基本表示

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

from django.urls import path
from . import views

app_name = 'board_app'

urlpatterns = [
    path('create_topic', views.create_topic, name='create_topic'),
    path('list_topics', views.list_topics, name='list_topics'),
    path('edit_topic/<int:id>', views.edit_topic, name='edit_topic'),
    path('delete_topic/<int:id>', views.delete_topic, name='delete_topic'),
    path('post_texts/<int:topic_id>', views.post_texts, name='post_texts'), # 追記箇所
]

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

from django import forms
from .models import Topics, Texts

class CreateTopicForm(forms.ModelForm):
  title = forms.CharField(label='Title')

  class Meta:
    model = Topics
    fields = ('title',)

class DeleteTopicForm(forms.ModelForm):

  class Meta:
    model = Topics
    fields = []

class PostTextForm(forms.ModelForm): # 追記箇所
  text = forms.CharField(
    label='', 
    widget=forms.Textarea(attrs={
      'rows':10,
      'cols':50
      })
    )
    
  class Meta:
    model = Texts
    fields = ('text',)

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

from django.http.response import Http404
from django.shortcuts import render, redirect, get_object_or_404
from . import forms
from django.contrib import messages
from .models import Topics
from django.http import Http404

def create_topic(request):
  create_topic_form = forms.CreateTopicForm(request.POST or None)
  if create_topic_form.is_valid():
    create_topic_form.instance.user = request.user
    create_topic_form.save()
    messages.success(request, 'You have been created board')
    return redirect('board_app:list_topics')
  return render(
    request, 'board_app/create_topic.html', context={
      'create_topic_form': create_topic_form,
    }
  )


def list_topics(request):
  topics = Topics.objects.pick_all_topics()
  return render(
    request, 'board_app/list_topics.html', context={
      'topics': topics
    }
  )

def edit_topic(request, id):
  topic = get_object_or_404(Topics, id=id)
  if topic.user.id != request.user.id:
    raise Http404
  edit_topic_form = forms.CreateTopicForm(request.POST or None, instance=topic)
  if edit_topic_form.is_valid():
    edit_topic_form.save()
    messages.success(request, 'You have been updated board')
    return redirect('board_app:list_topics')
  return render(
    request, 'board_app/edit_topic.html', context={
      'edit_topic_form': edit_topic_form,
      'id': id,
    }
  )

def delete_topic(request, id):
  topic = get_object_or_404(Topics, id=id)
  if topic.user.id != request.user.id:
    raise Http404
  delete_topic_form = forms.DeleteTopicForm(request.POST or None)
  if delete_topic_form.is_valid():
    topic.delete()
    messages.success(request, 'You have been deleted board')
    return redirect('board_app:list_topics')
  return render(
    request, 'board_app/delete_topic.html', context={
      'delete_topic_form': delete_topic_form
    }
  )

def post_texts(request, topic_id): # 追記箇所
  post_text_form = forms.PostTextForm(request.POST or None)
  topic = get_object_or_404(Topics, id=topic_id)
  if post_text_form.is_valid():
    post_text_form.instance.topic = topic
    post_text_form.instance.user = request.user
    post_text_form.save()
    return redirect('board_app:post_texts', topic_id=topic_id)
  return render(
    request, 'board_app/post_texts.html', context={
      'post_text_form': post_text_form,
      'topic': topic,
    }
  )

“board_app/views.py"で"post_texts.html"についてのコードを作成したので,"boardapptest_project/templates/board_app"に"post_texts.html"を以下のように作成する.

“boardapptest_project/templates/board_app/post_texts.html"を以下のように編集する.

{% extends "base.html" %}
{% block content %}
<h2>{{ topic.title }}</h2>
<form method="POST">
{% csrf_token %}
{{ post_text_form.as_p }}
<input type = "submit" value="sending text">
</form>
{% endblock %}

“boardapptest_project/templates/board_app/list_topics.html"を以下のように編集する.

{% extends "base.html" %}
{% block content %}
{% if messages %}
  {% for message in messages %}
  <div>{{ message.message }}</div>
  {% endfor %}
{% endif %}
<h2>page for board list</h2>
<p><a class="navbar-brand" href="{% url 'board_app:create_topic' %}">New board creation</a></p>
<table class="table table-striped table-hover">
  <thread>
    <tr>
      <th>No.</th>
      <th>Title</th>
      <th>Author</th>
    </tr>
  </thread>
  <tbody>
  {% for topic in topics %}
    <tr>
      <td>{{ forloop.counter }}</td>
      <td><a href="{% url 'board_app:post_texts' topic_id=topic.id %}">{{ topic.title }}</a></td> #変更箇所
      {% if topic.user.id == user.id %}
        <td><a href="{% url 'board_app:edit_topic' id=topic.id %}">{{ topic.user }}</a></td>
      {% else %}
        <td>{{ topic.user }}</td>
      {% endif %}
    </tr>
  {% endfor %}
  </tbody>
</table>
{% endblock %}

ターミナルを開き,仮想環境に移行し,"boardapptest_project"のディレクトリにて,"python manage.py runserver"を実行する.以下が出力される.

(djangoenv) C:\Users\shiro\Desktop\210517_python development\myboardapptest\boardapptest_project
>python manage.py runserver
Watching for file changes with StatReloader
Performing system checks...

System check identified no issues (0 silenced). # 以下出力箇所
August 13, 2021 - 18:55:44
Django version 3.2.3, using settings 'boardapptest_project.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/boardapptest_app"を入力すると以下画面に遷移する."Board list"をクリックする.

以下画面に遷移する."Title"がリンクになっている."board 4″をクリックする

以下画面に遷移し,テキストを入力することができる.

以下のようにテキストを入力し,"sending text"をクリックする.

VS Codeに戻り,"SQLITE EXPLORER"を開く."texts"テーブルを右クリックし,"Show Table"をクリックすると以下のように,上記で入力したテキストを確認できる.

(ii) テキストの履歴表示

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

from django.db import models

class TopicsManager(models.Manager):

  def pick_all_topics(self):
    return self.order_by('id').all()

class Topics(models.Model):

  title = models.CharField(max_length=100)
  user = models.ForeignKey(
    'boardapptest_app.User', on_delete=models.CASCADE
  )

  objects = TopicsManager()

  class Meta:
    db_table = 'topics'

class TextsManager(models.Manager): # 追記箇所(20~22行目)
  def pick_by_topic_id(self, topic_id):
    return self.filter(topic_id=topic_id).order_by('id').all()

class Texts(models.Model):

  text = models.CharField(max_length=500)
  user = models.ForeignKey(
    'boardapptest_app.User', on_delete=models.CASCADE
  )
  topic = models.ForeignKey(
    'Topics', on_delete=models.CASCADE
  )
  objects = TextsManager() # 追記箇所

  class Meta:
    db_table = 'texts'

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

from django.http.response import Http404
from django.shortcuts import render, redirect, get_object_or_404
from . import forms
from django.contrib import messages
from .models import Topics, Texts # 変更箇所
from django.http import Http404

def create_topic(request):
  create_topic_form = forms.CreateTopicForm(request.POST or None)
  if create_topic_form.is_valid():
    create_topic_form.instance.user = request.user
    create_topic_form.save()
    messages.success(request, 'You have been created board')
    return redirect('board_app:list_topics')
  return render(
    request, 'board_app/create_topic.html', context={
      'create_topic_form': create_topic_form,
    }
  )

def list_topics(request):
  topics = Topics.objects.pick_all_topics()
  return render(
    request, 'board_app/list_topics.html', context={
      'topics': topics
    }
  )

def edit_topic(request, id):
  topic = get_object_or_404(Topics, id=id)
  if topic.user.id != request.user.id:
    raise Http404
  edit_topic_form = forms.CreateTopicForm(request.POST or None, instance=topic)
  if edit_topic_form.is_valid():
    edit_topic_form.save()
    messages.success(request, 'You have been updated board')
    return redirect('board_app:list_topics')
  return render(
    request, 'board_app/edit_topic.html', context={
      'edit_topic_form': edit_topic_form,
      'id': id,
    }
  )

def delete_topic(request, id):
  topic = get_object_or_404(Topics, id=id)
  if topic.user.id != request.user.id:
    raise Http404
  delete_topic_form = forms.DeleteTopicForm(request.POST or None)
  if delete_topic_form.is_valid():
    topic.delete()
    messages.success(request, 'You have been deleted board')
    return redirect('board_app:list_topics')
  return render(
    request, 'board_app/delete_topic.html', context={
      'delete_topic_form': delete_topic_form
    }
  )

def post_texts(request, topic_id):
  post_text_form = forms.PostTextForm(request.POST or None)
  topic = get_object_or_404(Topics, id=topic_id)
  texts = Texts.objects.pick_by_topic_id(topic_id) # 追記箇所
  if post_text_form.is_valid():
    post_text_form.instance.topic = topic
    post_text_form.instance.user = request.user
    post_text_form.save()
    return redirect('board_app:post_texts', topic_id=topic_id)
  return render(
    request, 'board_app/post_texts.html', context={
      'post_text_form': post_text_form,
      'topic': topic,
      'texts': texts, # 追記箇所
    }
  )

“templates/board_app/post_texts.html"を以下のように編集する.

{% extends "base.html" %}
{% block content %}
<h2>{{ topic.title }}</h2>

{% for text in texts %} # 追記箇所(5~11行目)
{% if text.user.picture %}
  <img width="50pc" height="50px" src="{{ text.user.picture.url }}">
{% endif %}
<p>User name: {{ text.user.username }}</p>
<p>{{ text.text | linebreaks }}</p>
{% endfor %}

<form method="POST">
{% csrf_token %}
{{ post_text_form.as_p }}
<input type = "submit" value="sending text">
</form>
{% endblock %}

ターミナルを開き,仮想環境に移行し,"boardapptest_project"のディレクトリにて,"python manage.py runserver"を実行する.以下が出力される.

(djangoenv) C:\Users\shiro\Desktop\210517_python development\myboardapptest\boardapptest_project
>python manage.py runserver
Watching for file changes with StatReloader
Performing system checks...

System check identified no issues (0 silenced). # 以下出力箇所
August 13, 2021 - 18:55:44
Django version 3.2.3, using settings 'boardapptest_project.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/boardapptest_app"を入力すると以下画面に遷移する."Board list"をクリックする.

以下のように掲示板リストに遷移する.以下の"board 2″をクリックする.

以下画面に遷移する.

テキスト枠内に以下のように記述し,"sending text"をクリックする.

クリック後,user nameとテキストが赤枠のように出力される.

以下ページの赤枠の"Edit info"をクリックする.

以下のようにユーザー情報のページに遷移する."ファイルを選択"をクリックし,写真を選択する.

写真を選択すると,"ファイルを選択"の右にjpgの名前が出力される."update"をクリックする.

問題がないと,赤枠のメッセージが出力される."Board list"をクリックする.

以下画面に遷移する."board 2″をクリックする.

“board 2″のページでは,画像が出力される準備ができた.コードを記述すると当該ページで画像を出力できる.以下に画像を出力するコードを記述する.

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

from django.contrib import admin
from django.urls import path, include
from django.conf import settings # 追記箇所(3~4行目)
from django.conf.urls.static import static

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

if settings.DEBUG: # 以下追記箇所
    urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

上記を保存し,"board 2″のページに遷移する.アップロードした画像を確認することができる.

“templates/board_app/post_texts.html"を以下のように編集する.

{% extends "base.html" %}
{% block content %}
<h2>{{ topic.title }}</h2>

{% for text in texts %}
<div class="col-1 offset-1">
  {% if text.user.picture %}
    <img style="float:left;" width="50pc" height="50px" src="{{ text.user.picture.url }}">
  {% endif %}
</div>
<div class="col-5 offset-2">
  <p>User name: {{ text.user.username }}</p>
  <p>{{ text.text | linebreaks }}</p>
</div>
<div class="col-5 offset-1">
<hr>
</div>
{% endfor %}
<div class="col-5 offset-1">
  <form method="POST">
  {% csrf_token %}
  {{ post_text_form.as_p }}
  <input type = "submit" value="sending text">
  </form>
</div>
{% endblock %}

上記を保存し,"board 2″のページに遷移すると,以下のようなレイアウトになる.

“Logout"をクリックし,別のユーザーでログインして,"board 2″のページに書き込みができるかを確認した.以下のように問題なく書き込みができることを確認できた.

現状ログイン状態でなければ,"board list"を確認できない."templates/base.html"を以下のように編集すると,ログインしなくても"board list"を確認することができる.

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<!-- bootstrap -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<a class="navbar-brand" href="{% url 'boardapptest_app:home' %}">Home</a>
<a class="navbar-brand" href="{% url 'board_app:list_topics' %}">Board list</a> # 変更箇所(11~12行目;11行目のコードを"is_authenticated"の下から上へ変更した)
{% if user.is_authenticated %}
<a class="navbar-brand" href="{% url 'boardapptest_app:logout_page' %}">Logout</a>
<a class="navbar-brand" href="{% url 'boardapptest_app:edit_page' %}">Edit Info</a>
{% else %}
<a class="navbar-brand" href="{% url 'boardapptest_app:login_page' %}">Login</a>
<a class="navbar-brand" href="{% url 'boardapptest_app:registration' %}">User registration</a>
{% endif %}
</nav>
{% block content %}{% endblock %}
</body>
</html>

上記を保存し,"Logout"をクリックして,"board list"に遷移する.赤枠のようにログアウト状態にも関わらず,以下のように"board list"を確認できた.

ログアウト状態で"board list"に移行することはできる.ただ,以下赤枠のようにテキストを送信できる状態になっている.

以下にて,ログアウト状態でテキストを送信できないように実装する."templates/board_app/post_texts.html"を以下のように編集する.

{% extends "base.html" %}
{% block content %}
<h2>{{ topic.title }}</h2>

{% for text in texts %}
<div class="col-1 offset-1">
  {% if text.user.picture %}
    <img style="float:left;" width="50pc" height="50px" src="{{ text.user.picture.url }}">
  {% endif %}
</div>
<div class="col-5 offset-2">
  <p>User name: {{ text.user.username }}</p>
  <p>{{ text.text | linebreaks }}</p>
</div>
<div class="col-5 offset-1">
<hr>
</div>
{% endfor %}
{% if user.is_authenticated %} # 追記箇所
<div class="col-5 offset-1">
  <form method="POST">
  {% csrf_token %}
  {{ post_text_form.as_p }}
  <input type = "submit" value="sending text">
  </form>
</div>
{% endif %} # 追記箇所
{% endblock %}

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

from django.http.response import Http404
from django.shortcuts import render, redirect, get_object_or_404
from . import forms
from django.contrib import messages
from .models import Topics, Texts
from django.http import Http404

def create_topic(request):
  create_topic_form = forms.CreateTopicForm(request.POST or None)
  if create_topic_form.is_valid():
    create_topic_form.instance.user = request.user
    create_topic_form.save()
    messages.success(request, 'You have been created board')
    return redirect('board_app:list_topics')
  return render(
    request, 'board_app/create_topic.html', context={
      'create_topic_form': create_topic_form,
    }
  )

def list_topics(request):
  topics = Topics.objects.pick_all_topics()
  return render(
    request, 'board_app/list_topics.html', context={
      'topics': topics
    }
  )

def edit_topic(request, id):
  topic = get_object_or_404(Topics, id=id)
  if topic.user.id != request.user.id:
    raise Http404
  edit_topic_form = forms.CreateTopicForm(request.POST or None, instance=topic)
  if edit_topic_form.is_valid():
    edit_topic_form.save()
    messages.success(request, 'You have been updated board')
    return redirect('board_app:list_topics')
  return render(
    request, 'board_app/edit_topic.html', context={
      'edit_topic_form': edit_topic_form,
      'id': id,
    }
  )

def delete_topic(request, id):
  topic = get_object_or_404(Topics, id=id)
  if topic.user.id != request.user.id:
    raise Http404
  delete_topic_form = forms.DeleteTopicForm(request.POST or None)
  if delete_topic_form.is_valid():
    topic.delete()
    messages.success(request, 'You have been deleted board')
    return redirect('board_app:list_topics')
  return render(
    request, 'board_app/delete_topic.html', context={
      'delete_topic_form': delete_topic_form
    }
  )

def post_texts(request, topic_id):
  post_text_form = forms.PostTextForm(request.POST or None)
  topic = get_object_or_404(Topics, id=topic_id)
  texts = Texts.objects.pick_by_topic_id(topic_id)
  if post_text_form.is_valid():
    if not request.user.is_authenticated: # 追記箇所(65~66行目)
      raise Http404
    post_text_form.instance.topic = topic
    post_text_form.instance.user = request.user
    post_text_form.save()
    return redirect('board_app:post_texts', topic_id=topic_id)
  return render(
    request, 'board_app/post_texts.html', context={
      'post_text_form': post_text_form,
      'topic': topic,
      'texts': texts,
    }
  )

上記を保存し,ログアウト状態で"board list"に移行すると,以下のようにテキスト送信項目は無くなった.

  1. 一時保存機能の作成

以下URLをブラウザで開く.

https://code.jquery.com/

以下画面に遷移するので,赤枠のURLをクリックする.

クリックすると以下画面が出現するので,赤枠の"Copy"をクリックする.

“templates/base.html"を開き,上記でコピーしたコードを</head>の1行上に挿入し,以下のように編集する.

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<!-- bootstrap -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
<script src="https://code.jquery.com/jquery-3.6.0.js" integrity="sha256-H+K7U5CnXl1h5ywQfKtSj8PCmoN9aaq30gDh27Xc0jk=" crossorigin="anonymous"></script> # 追記箇所
</head>
<body>
  <nav class="navbar navbar-expand-lg navbar-light bg-light">
    <a class="navbar-brand" href="{% url 'boardapptest_app:home' %}">Home</a>
    <a class="navbar-brand" href="{% url 'board_app:list_topics' %}">Board list</a>
    {% if user.is_authenticated %}
    <a class="navbar-brand" href="{% url 'boardapptest_app:logout_page' %}">Logout</a>
    <a class="navbar-brand" href="{% url 'boardapptest_app:edit_page' %}">Edit Info</a>
    {% else %}
    <a class="navbar-brand" href="{% url 'boardapptest_app:login_page' %}">Login</a>
    <a class="navbar-brand" href="{% url 'boardapptest_app:registration' %}">User registration</a>
    {% endif %}
  </nav>
{% block content %}{% endblock %}
{% block javascript %}{% endblock %} # 追記箇所
</body>
</html>

“templates/board_app/post_texts.html"を以下のように編集する.

{% extends "base.html" %}
{% block content %}
<h2>{{ topic.title }}</h2>

{% for text in texts %}
<div class="col-1 offset-1">
  {% if text.user.picture %}
    <img style="float:left;" width="50pc" height="50px" src="{{ text.user.picture.url }}">
  {% endif %}
</div>
<div class="col-5 offset-2">
  <p>User name: {{ text.user.username }}</p>
  <p>{{ text.text | linebreaks }}</p>
</div>
<div class="col-5 offset-1">
<hr>
</div>
{% endfor %}
{% if user.is_authenticated %}
<div class="col-5 offset-1">
  <form method="POST">
  {% csrf_token %}
  {{ post_text_form.as_p }}
  <input type = "button" value="save" id="save_text">
  <input type = "submit" value="sending text">
  </form>
</div>
{% endif %}
{% endblock %}
{% block javascript %}
<script>
$("#save_text").click(function(){
  var text = $("#id_text").val();
  $.ajax({
    url: "{% url 'board_app:save_text' %}",
    type: "GET",
    data: {text: text, topic_id: "{{ topic.id }}"},
    dataType: "json",
    success: function(json){
      if(json.message){
        alert(json.message);
      }
    }
  });
});
</script>
{% endblock %}

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

from django.urls import path
from . import views

app_name = 'board_app'

urlpatterns = [
  path('create_topic', views.create_topic, name='create_topic'),
  path('list_topics', views.list_topics, name='list_topics'),
  path('edit_topic/<int:id>', views.edit_topic, name='edit_topic'),
  path('delete_topic/<int:id>', views.delete_topic, name='delete_topic'),
  path('post_texts/<int:topic_id>', views.post_texts, name='post_texts'),
  path('save_text', views.save_text, name='save_text'), # 追記箇所
]

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

from django.http.response import Http404
from django.shortcuts import render, redirect, get_object_or_404
from . import forms
from django.contrib import messages
from .models import Topics, Texts
from django.http import Http404
from django.core.cache import cache # 追記箇所(7~8行目)
from django.http import JsonResponse

def create_topic(request):
  create_topic_form = forms.CreateTopicForm(request.POST or None)
  if create_topic_form.is_valid():
    create_topic_form.instance.user = request.user
    create_topic_form.save()
    messages.success(request, 'You have been created board')
    return redirect('board_app:list_topics')
  return render(
    request, 'board_app/create_topic.html', context={
      'create_topic_form': create_topic_form,
    }
  )

def list_topics(request):
  topics = Topics.objects.pick_all_topics()
  return render(
    request, 'board_app/list_topics.html', context={
      'topics': topics
    }
  )

def edit_topic(request, id):
  topic = get_object_or_404(Topics, id=id)
  if topic.user.id != request.user.id:
    raise Http404
  edit_topic_form = forms.CreateTopicForm(request.POST or None, instance=topic)
  if edit_topic_form.is_valid():
    edit_topic_form.save()
    messages.success(request, 'You have been updated board')
    return redirect('board_app:list_topics')
  return render(
    request, 'board_app/edit_topic.html', context={
      'edit_topic_form': edit_topic_form,
      'id': id,
    }
  )

def delete_topic(request, id):
  topic = get_object_or_404(Topics, id=id)
  if topic.user.id != request.user.id:
    raise Http404
  delete_topic_form = forms.DeleteTopicForm(request.POST or None)
  if delete_topic_form.is_valid():
    topic.delete()
    messages.success(request, 'You have been deleted board')
    return redirect('board_app:list_topics')
  return render(
    request, 'board_app/delete_topic.html', context={
      'delete_topic_form': delete_topic_form
    }
  )

def post_texts(request, topic_id):
  saved_text = cache.get(f'saved_text-topic_id={topic_id}-user_id={request.user.id}', '') # 追記箇所
  post_text_form = forms.PostTextForm(request.POST or None, initial={'text':saved_text}) # 変更箇所
  topic = get_object_or_404(Topics, id=topic_id)
  texts = Texts.objects.pick_by_topic_id(topic_id)
  if post_text_form.is_valid():
    if not request.user.is_authenticated:
      raise Http404
    post_text_form.instance.topic = topic
    post_text_form.instance.user = request.user
    post_text_form.save()
    cache.delete(f'saved_text-topic_id={topic_id}-user_id={request.user.id}') # 追記箇所
    return redirect('board_app:post_texts', topic_id=topic_id)
  return render(
    request, 'board_app/post_texts.html', context={
      'post_text_form': post_text_form,
      'topic': topic,
      'texts': texts,
    }
  )

def save_text(request): # 以下追記箇所
  if request.is_ajax: # (Django 4.0以降の場合,is_ajax => accepts に変更)
    text = request.GET.get('text')
    topic_id = request.GET.get('topic_id')
    if text and topic_id:
      cache.set(f'saved_text-topic_id={topic_id}-user_id={request.user.id}', text)
      return JsonResponse({'message':'temporarily saved'})

ターミナルを開き,仮想環境のディレクトリ"boardapptest_project"にて,"python manage.py runserver"を実行する.実行後,以下が出力される.

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

System check identified no issues (0 silenced). # 以下出力箇所
August 15, 2021 - 03:42:10
Django version 3.2.3, using settings 'boardapptest_project.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/boardapptest_app"を入力すると以下画面に遷移する.ログイン後,以下の"Board list"をクリックする.

以下掲示板一覧画面に遷移する."board 2″をクリックする.

以下"board 2″の画面に遷移する.

以下"board 2″の画面に遷移する.テキスト欄に"test2″と記述し,"save"をクリックすると"temporarily saved"のポップアップが出現する.

掲示板一覧画面に戻り,"board 1″の画面に遷移し,上記と同じように."test1″と記述し,"save"をクリックすると"temporarily saved"のポップアップが出現する.

“board 2″の画面に戻っても,記述したテキスト"test2″は残っているので成功となる.

“board 1″の画面に戻っても,記述したテキスト"test1″は残っているので,成功となる.

  1. エラー対応機能の作成

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

from django.shortcuts import render, redirect
from . import forms
from django.core.exceptions import ValidationError
from .models import UserActiveTokens
from django.contrib.auth import authenticate, login, logout
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.contrib.auth import update_session_auth_hash

def home(request):
  return render(
    request, 'boardapptest_app/home.html'
  )

def registration(request):
  registration_form = forms.RegistrationForm(request.POST or None)
  if registration_form.is_valid():
    try:
      registration_form.save()
      return redirect('boardapptest_app:home')
    except ValidationError as e:
      registration_form.add_error('password', e)     
  return render(
    request, 'boardapptest_app/registration.html', context={
      'registration_form':registration_form,
    }
  )

def active_user(request, token):
  user_active_token = UserActiveTokens.objects.active_user_using_token(token)
  return render(
    request, 'boardapptest_app/active_user.html'
  )

def login_page(request):
  login_form = forms.LoginForm(request.POST or None)
  if login_form.is_valid():
    email = login_form.cleaned_data.get('email')
    password = login_form.cleaned_data.get('password')
    r_user = authenticate(email=email, password=password)
    if r_user:
      if r_user.is_active:
        login(request, r_user)
        messages.success(request, 'You are successfully logged in')
        return redirect('boardapptest_app:home')
      else:
        messages.warning(request, 'user is not valid')
    else:
      messages.warning(request, 'user or password is wrong')
  return render(
    request, 'boardapptest_app/login_page.html', context={
      'login_form':login_form,
    }
  )

@login_required
def logout_page(request):
  logout(request)
  messages.success(request, 'You have been logged out')
  return redirect('boardapptest_app:home')

@login_required
def edit_page(request):
  edit_form = forms.UserEditForm(
    request.POST or None, 
    request.FILES or None, 
    instance = request.user
    )
  if edit_form.is_valid():
    messages.success(request, 'You have been updated')
    edit_form.save()
  return render(request, 'boardapptest_app/edit_page.html', context={
      'edit_form': edit_form,
  })

@login_required
def change_password(request):
  change_password_form = forms.ChangePasswordForm(request.POST or None, instance=request.user)
  if change_password_form.is_valid():
    try:
      change_password_form.save()
      messages.success(request, 'You have been updated password')
      update_session_auth_hash(request, request.user)
    except ValidationError as e:
      change_password_form.add_error('password', e)
  return render(
    request, 'boardapptest_app/change_password.html', context={
      'change_password_form': change_password_form,
    }
  )

def error_page(request, exception): # 以下追記箇所
  return render(
    request, '404.html'
  )

“board_app/views.py"で"404.html"を作成したので,以下のように"templates"に"404.html"を作成する.

“templates/404.html"を以下のように編集する.

{% extends "base.html" %}
{% block content %}
There was an error!
{% endblock %}

“boardapptest_project/setting.py"を以下のように変更する.

DEBUG = False # 変更箇所

ALLOWED_HOSTS = ['127.0.0.1'] # 変更箇所

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

from django.contrib import admin
from django.urls import path, include
from django.conf import settings
from django.conf.urls.static import static
from boardapptest_app.views import error_page # 追記箇所

urlpatterns = [
  path('admin/', admin.site.urls),
  path('boardapptest_app/', include('boardapptest_app.urls')),
  path('board_app/', include('board_app.urls')),
]
handler404 = error_page # 追記箇所

if settings.DEBUG:
  urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

ターミナルを開き,仮想環境のディレクトリ"boardapptest_project"にて,"python manage.py runserver"を実行する.実行後,以下が出力される.

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

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

上記の"http://127.0.0.1:8000/"をクリックし,ブラウザを開く.通常だとエラー画面が出力されるが,エラー画面に遷移させることができた.

他のエラー画面も確認してみる.以下のように"board 2″のページに遷移する.URLの後ろに番号が付与されている.

エラーが出力されるようにURLの後ろの番号を変更すると,以下のようにエラーページに遷移した.

  1. 反省点

アプリ名を"boardapptest_app"としていたが,アカウントを作成するので,アプリ名は"account_app"がベターだと思う.

■参照

https://qiita.com/t_toriumi/items/50bad616d35f07e3340c

https://docs.djangoproject.com/ja/3.1/topics/cache/

https://code.jquery.com/

以上

PythonDjango,掲示板

Posted by クマガイ