Python | Django | 掲示板の作成方法
公開日:2021/8/16
Pythonには,DjangoというWebアプリケーションフレームワークがある.フレームワークのため,Djangoを利用するとWebアプリを通常よりも短時間で開発することが可能になる.
本記事では,「掲示板の作成方法」を以下8項目にて記す.
- ホーム画面の作成
- ユーザー登録の作成
- ログインの作成
- ユーザー編集画面の作成
- 掲示板画面の作成
(1) タイトル作成の実装
(2) 掲示板一覧ページの実装
(3) 掲示板編集ページの実装
(4) 掲示板削除ページの実装
(5) テキスト作成の実装 - 一時保存機能の作成
- エラー対応機能の作成
- 反省点
◆実施環境
Python 3.8.8
Django 3.2.3
■掲示板の作成方法
新たにフォルダを開く.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"を入力すると以下画面に遷移する.
“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″となるのを確認できる.
“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"をクリックすると,以下画面に遷移する.
“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"のパスワードが正しく,複雑であると,以下赤枠のメッセージが出力される.パスワードの変更が成功となる.
“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″をデータベースのテーブルにて確認することができる.
“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″が一覧に追加される.
“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のみがリンクになっている.
“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が削除された.
(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"に移行すると,以下のようにテキスト送信項目は無くなった.
以下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″は残っているので,成功となる.
“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の後ろの番号を変更すると,以下のようにエラーページに遷移した.
アプリ名を"boardapptest_app"としていたが,アカウントを作成するので,アプリ名は"account_app"がベターだと思う.
■参照
https://qiita.com/t_toriumi/items/50bad616d35f07e3340c
https://docs.djangoproject.com/ja/3.1/topics/cache/
https://code.jquery.com/
以上