【Django3.1】generic viewを利用してCRUDを行う方法
- 本記事で行うこと
- モチベーション
- 環境
- 事前準備 pyenv環境
- Djangoのインストール
- Django 初期設定
- CRUD用のDBを作成
- Read画面の作成
- Create画面の作成
- Update画面の作成
- Dalete画面の作成
本記事で行うこと
GitHub - n-guitar/django-samples at feature/first/sample.generic-view
モチベーション
- Djangoを教える時のサンプルとして利用したいため。
環境
$ sw_vers ProductName: macOS ProductVersion: 11.1 BuildVersion: 20C69
- Python 3.8.0
$ python -V Python 3.8.0
事前準備 pyenv環境
$ python -m venv env $ source env/bin/activate $ export PATH=$PWD/env/bin:$PATH
Djangoのインストール
$ pip install django==3.1.3
Django 初期設定
- プロジェクトとアプリを作成
- プロジェクトでtempletsフォルダを設定
$ django-admin startproject confug . $ django-admin startapp samplecrud $ mkdir templates
- settings.py編集 appの追加とtemplateフォルダを参照するように編集
confug/settings.py
・・・ # samplecrudを追加 INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'samplecrud', # 追加 ] ・・・ # TEMPLATESのDIRSを変更 TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': ['templates'], # 変更 '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', ], }, }, ]
動作確認
$ python manage.py runserver
http://127.0.0.1:8000/
にアクセスして以下が表示されればOK
以上で準備完了。
一旦control + cで止めておく。
現時点で以下のようなディレクトリ構成になっている。
CRUD用のDBを作成
今回は以下のようなDBを作成する
Column | Field |
---|---|
title | CharField(max_length=100) |
memo | TextField(max_length=255) |
samplecrud/models.py
from django.db import models class Todo(models.Model): title = models.CharField(max_length=100) memo = models.TextField(max_length=255) def __str__(self): return self.title
DBの反映
$ python manage.py makemigrations samplecrud Migrations for 'samplecrud': samplecrud/migrations/0001_initial.py - Create model Todo $ python manage.py migrate samplecrud Operations to perform: Apply all migrations: samplecrud Running migrations: Applying samplecrud.0001_initial... OK
Read画面の作成
早速Read画面を作成するがその前に、URLのつなぎこみを行う。
※直接configからURLを指定してもいいが、通常アプリごとに作成する多い。
URLのつなぎこみ
$ touch samplecrud/urls.py
現時点のディレクトリ構造は以下の通りになっている。
URLのつなぎこみ。
config/urls.pyを編集する
from django.contrib import admin from django.urls import path, include # include追加 urlpatterns = [ path('admin/', admin.site.urls), path('samplecrud/', include('samplecrud.urls')), # 追加 ]
上記のURLは『http://127.0.0.1:8000/samplecrud/』でアクセスした時、『samplecrud/urls.py』記載されているURLにルーティングするという意味。
samplecrud/urls.pyを編集する
※現時点ではListPageはviewsの中に存在していないため、エラーになるがこの次に作成する。
from django.urls import path from .views import ListPage urlpatterns = [ path('list/', ListPage.as_view(), name='list'), ]
Read画面の作成
いよいよ画面を作成していくが CRUDを実現するのにdjango.viewsのgeneric viewと呼ばれるもの利用する。 マニュアルはあるが、ソースコードを見たほうが早い場合があるので必要に応じて参照する。
generic.ListViewを継承したクラスを作り、どのDBのTableを利用して、どのhtmlファイルにレンダリングするかを以下のように書くだけ。
samplecrud/views.py
from django.shortcuts import render from .models import Todo from django.views import generic class ListPage(generic.ListView): model = Todo template_name = 'list.html'
レンダリング先のhtmlがないので作成する。
templates/list.htmlの作成
<h1>list Page</h1> <ul> {% for item in object_list %} <li>{{ item.title }}: {{ item.memo }}</li> {% endfor %} </ul>
Todo tableから取得した値がobject_listというオブジェクトに格納され、list.htmlに渡される
この状態でもう一度runserverし
$ python manage.py runserver
http://127.0.0.1:8000/samplecrud/list/ にアクセスする
以下のように表示されればOK。
今はテーブルに何もレコードがないため、何も表示されない。
Create画面の作成
続けてDBのレコードを追加する画面を作成する。
以下のファイルをそれぞれ編集する。
samplecrud/urls.py
from django.urls import path from .views import ListPage, CreatePage # 追記 samplecrud/urls.py urlpatterns = [ path('list/', ListPage.as_view(), name='list'), path('create/', CreatePage.as_view(), name='create'), # 追記 ]
こちらは先程のlistと同じ
samplecrud/views.py
from django.shortcuts import render from .models import Todo from django.views import generic from django.urls import reverse_lazy class ListPage(generic.ListView): model = Todo template_name = 'list.html' class CreatePage(generic.CreateView): model = Todo template_name = 'create.html' fields = ('title','memo') success_url = reverse_lazy('list')
createのときはどのFieldを更新するか『fields』で指定する。
『success_url = reverse_lazy('list')』はcreateが成功したときにどの画面を表示するかするかを示す。
ちはみに『reverse_lazy('list')』のlistはsamplecrud/urls.pyのnameに指定した名前を利用できる。
templates/create.html
<h1>Create Page</h1> <form action="" method="POST"> {% csrf_token %} {{ form.as_p}} <input type="submit" value="作成する"> </form>
Djangoは『 {% csrf_token %}』をつけることでデフォルトでCSRFの検証を行ってくれる。(逆につけないとエラーになる)
『{{ form.as_p}}』は fieldsで指定した項目を
タグで展開してくれる。(余談だがCreateViewがForm classを継承しているため利用できる)
この状態でもう一度runserverし
$ python manage.py runserver
http://127.0.0.1:8000/samplecrud/create/
にアクセスすると以下によう表示され、更新するとlistが表示されて先程更新したデータが表示される。
何件か登録してみるとすべて表示されることがわかる。
Update画面の作成
続けてDBのレコードを更新する画面を作成する。
以下のファイルをそれぞれ編集する。
samplecrud/urls.py
from django.urls import path from .views import ListPage, CreatePage, UpdatePage # 追記 urlpatterns = [ path('list/', ListPage.as_view(), name='list'), path('create/', CreatePage.as_view(), name='create'), path('update/<int:pk>', UpdatePage.as_view(), name='update'), # 追記 ]
こちらは先程と若干異なり、URL Pathに/<int:pk>が記載されている。
これはDBのどのレコードを更新していいか判断できないので<int:pk>で一意にレコードを特定する。
※DjangoではデフォルトでTableを作成するとidというPrimary KeyになるColumnが作成される。
samplecrud/views.py
from django.shortcuts import render from .models import Todo from django.views import generic from django.urls import reverse_lazy class ListPage(generic.ListView): model = Todo template_name = 'list.html' class CreatePage(generic.CreateView): model = Todo template_name = 'create.html' fields = ('title','memo') success_url = reverse_lazy('list') class UpdatePage(generic.UpdateView): model = Todo template_name = 'update.html' fields = ('title','memo') success_url = reverse_lazy('list')
createのとき同じでFieldを更新するか『fields』で指定する。
templates/update.html
<h1>Update Page</h1> <form action="" method="POST"> {% csrf_token %} {{ form.as_p}} <input type="submit" value="更新する"> </form>
create.htmlと全く同じで問題ない。(valueの値だけ更新するに変更した。)
この状態でもう一度runserverし
$ python manage.py runserver
http://127.0.0.1:8000/samplecrud/update/1
にアクセスすると以下によう表示され、更新するとlistが表示されて先程更新したデータが表示される。
しかしながら、『update/1』のURL Pathに/<int:pk>がに当たる数字を画面上表示していないため、わかりにくいので、
listページからそれぞれのレコードに対してリンクを貼ることにする。
Listページからのリンクを作成
templates/list.html
<h1>list Page</h1> <ul> {% for item in object_list %} <li> {{ item.title }}: {{ item.memo }} <a href="{% url 'update' item.pk %}">編集する</a> </li> {% endfor %} </ul>
aタグを使い、リンクを作成する。
ここでもurl指定はsamplecrud/urls.pyのnameに指定した名前を利用できる。
また、どのレコードか一意に特定するため、『item.pk』でパラメータをしていることがポイントとなる。
この状態でもう一度runserverし
$ python manage.py runserver
http://127.0.0.1:8000/samplecrud/list/ を表示すると以下の用になり、クリックするとupdate画面に移動できる。
Dalete画面の作成
最後にDBのレコードを削除する画面を作成する。
以下のファイルをそれぞれ編集する。
samplecrud/urls.py
from django.urls import path from .views import ListPage, CreatePage, UpdatePage, DeletePage # 追記 urlpatterns = [ path('list/', ListPage.as_view(), name='list'), path('create/', CreatePage.as_view(), name='create'), path('update/<int:pk>', UpdatePage.as_view(), name='update'), path('delete/<int:pk>', DeletePage.as_view(), name='delete'), # 追記 ]
updateと同じく、どのレコードかを特定する/<int:pk>を指定する。
samplecrud/views.py
from django.shortcuts import render from .models import Todo from django.views import generic from django.urls import reverse_lazy class ListPage(generic.ListView): model = Todo template_name = 'list.html' class CreatePage(generic.CreateView): model = Todo template_name = 'create.html' fields = ('title','memo') success_url = reverse_lazy('list') class UpdatePage(generic.UpdateView): model = Todo template_name = 'update.html' fields = ('title','memo') success_url = reverse_lazy('list') class DeletePage(generic.DeleteView): model = Todo template_name = 'delete.html' success_url = reverse_lazy('list')
deleteの場合は一度削除してよいかもう一度確認する画面を表示する。
※余談だが、DeleteViewは今回登場させていない1レコードの詳細表示されるDetailViewを継承しているので、DetailViewを兼ねることもできる。
(Detail/Deleteでそれぞれ複数の同じページを作成させたくない時や、Deleteの確認をポップアップ表示させたい時などにDeleteViewをDetailViewを兼ねて自分は利用する)
templates/delete.html
<h1>Delete Page</h1> <form action="" method="POST"> {% csrf_token %} <p> {{ object.title }}{{ object.memo }} を本当に削除しますか? </p> <input type="submit" value="削除する"> </form>
listからdeleteへのリンクもupdate同様作成しておく
<h1>list Page</h1> <ul> {% for item in object_list %} <li> {{ item.title }}: {{ item.memo }} <a href="{% url 'update' item.pk %}">編集する</a> <a href="{% url 'delete' item.pk %}">削除する</a> </li> {% endfor %} </ul>
この状態でもう一度runserverし
$ python manage.py runserver
http://127.0.0.1:8000/samplecrud/list/ を表示すると以下の用になり、削除をクリックするとレコードが削除される。