djangoで作るTodo管理アプリ⑨(CreateView、UpdateView、DeleteView)

django

はじめに

前回は、ListViewとDetailViewを用いて登録したTodoの一覧表示と詳細を表示しました。
今回は、CreateView(登録)とUpdateView(更新)とDeleteView(削除)を用いて、Todoアプリに必要のTodoの登録、更新、削除の機能を実装します。

今回でdjangoで作るTodoアプリは完成します。
Todo管理のWebアプリの動作イメージは下記となります。

参考にした書籍

bookfan 1号店 楽天市場店
¥3,300 (2024/09/26 11:26時点 | 楽天市場調べ)

Todoを登録する際の動作フロー

Todoを登録するときの動作フローは下記となります。Todoを更新する際も同様の動作になります。
テンプレートシステムを用いることで、各ページで共通して使用しているヘッダーやボタンの記述をbase.html、_form.htmlにまとめています。

  1. ユーザーがブラウザを通じて特定のURLにアクセスします。
  2. Webサーバーが受け取ったHTTPリクエストをdjangoのルーティングシステム(urls.py)に渡します。
  3. プロジェクトのurls.pyがリクエストを適切なアプリケーション(この場合はtodocreate)のurls.pyにルーティングします。
  4. アプリ(この場合はtodocreate)のurls.pyはリクエストを関連するビュー(views.py)に渡します。
  5. ビューは、必要に応じてデータベースからデータを取得するためのクエリをデータベースに送信し、情報を取得します。
  6. データベースから取得した情報、commonのforms.py、base.html、create.html、_form.htmlをビュー(views.py)によって組み合わせられます。
  7. ビュー(views.py)は最終的なHTTPレスポンスを生成し、Webサーバーに返します。
  8. WebサーバーはこのHTTPレスポンスをユーザーのブラウザに送信し、ユーザーはTodo更新ページを表示できます。

djangoの各アプリの役割

Todoアプリの機能ごとに、djangoのアプリを用意しています。

アプリ名 概要
common ・Todoアプリで使用するモデル(データ)を定義する
・Todoアプリで使用するformタグの詳細を定義する
todolist ・登録したTodoの一覧を表示する
tododetail ・登録したTodoの詳細を表示する
todoupdate ・登録したTodoの内容を更新する
tododelete ・登録したTodoを削除する
todocreate ・Todoを登録する

更新/新規作成するファイル

Todoアプリを作成するために更新、新規作成したファイルの一覧です。

フォルダ名 ファイル名 更新/新規作成 コードの概要
todo_project urls.py 更新 ・httpリクエストのルーティングを設定
・リダイレクトの設定
settings.p 更新 ・INSTALLED_APPSにアプリを登録
・TEMPLATESにBASE_DIRを設定
common models.py 更新 ・タイトル、作業内容、優先度、期日を設定
・優先度の選択は高、通常、低の3つ
forms.py 新規作成 ・作業内容の入力フォーム表示は5行
・優先度の選択はプルダウン
・期日の選択はカレンダー
todolist urls.py 新規作成 ・views.py内のTodoListTopを呼び出す
views.py 更新 ・TodoListTopクラスの設定
・template_nameとmodelを設定
tododetail urls.py 新規作成 ・views.py内のTodoDetailを呼び出す
views.py 更新 ・TodoDetailクラスを設定
・template_nameとmodelを設定
todoupdate urls.py 新規作成 ・views.py内のTodoUpdateを呼び出す
views.py 更新 ・TodoUpdateクラスを設定
・template_nameとmodelを設定
・使用する入力フォームの設定
・更新に成功したときの遷移先の設定
tododelete urls.py 新規作成 ・views.py内のTodoDetailを呼び出す
views.py 更新 ・TodoDetailクラスを設定
・削除に成功したときの遷移先の設定
todocreate urls.py 新規作成 ・views.py内のTodoCreateを呼び出す
views.py 更新 ・TodoCreateクラスを設定
・template_nameとmodelを設定
・使用する入力フォームの設定
・更新に成功したときの遷移先の設定
templates _form.html 新規作成 ・作成、更新の入力フォームを定義
base.html 新規作成 ・全てのページのベースとなるhtml
・bootstrap5.3を読み込んでいる
create.html 新規作成 ・作成のページ
detail.html 新規作成 ・詳細のページ
list.html 新規作成 ・一覧のページ
update.html 新規作成 ・更新のページ

開発環境の用意

開発環境はVisual Studio Codeを使用します。djangoの実行環境はWindows上で動作させたUbuntuで実行します。WindowsにUbuntu環境を構築する手順は下記を参照してください。

djangoの入門①(djangoの基本と環境構築)

  • Pythonの実行環境を構築済みの場合は、下記を実施する必要はありません。
$ sudo apt update
$ sudo apt upgrade
$ sudo apt install python3.12
$ sudo apt install python3.12-venv
  • djangoを実行する仮想環境を作成します。
$ mkdir todo
$ cd todo
$ python3.12 -m venv venv
  • django4.2.9をインストールします。
$ source ./venv/bin/active
(venv)$ pip install --upgrade pip
(venv)$ pip install django==4.2.9
  • Todoアプリのプロジェクト、アプリを作成します。
(venv)$ django-admin startproject todo_project .
(venv)$ python manage.py startapp todolist
(venv)$ python manage.py startapp tododetail
(venv)$ python manage.py startapp common
(venv)$ python manage.py startapp todoupdate
(venv)$ python manage.py startapp tododelete
(venv)$ python manage.py startapp todocreate
  • Todoアプリで使用するディレクトリ、ファイルを作成します。
(venv)$ mkdir templates
(venv)$ touch ./common/forms.py
(venv)$ touch ./todolist/urls.py
(venv)$ touch ./tododetail/urls.py
(venv)$ touch ./tododelete/urls.py
(venv)$ touch ./todocreate/urls.py
(venv)$ touch ./todoupdate/urls.py
(venv)$ touch ./templates/_form.html
(venv)$ touch ./templates/base.html
(venv)$ touch ./templates/create.html
(venv)$ touch ./templates/detail.html
(venv)$ touch ./templates/list.html
(venv)$ touch ./templates/update.html
  • 開発環境の構築を終えると、下記のファイル・ディレクトリ構成となります。
(venv)$ cd ..
(venv)$ tree -L 3
.
└── todoapp
    ├── LICENSE
    ├── README.md
    ├── common
    │    ├── __init__.py
    │    ├── __pycache__
    │    ├── admin.py
    │    ├── apps.py
    │    ├── forms.py
    │    ├── migrations
    │    ├── models.py
    │    ├── tests.py
    │    └── views.py
    ├── db.sqlite3
    ├── forms.py
    ├── manage.py
    ├── templates
    │    ├── _form.html
    │    ├── base.html
    │    ├── create.html
    │    ├── detail.html
    │    ├── list.html
    │    └── update.html
    ├── todo_project
    │    ├── __init__.py
    │    ├── __pycache__
    │    ├── asgi.py
    │    ├── settings.py
    │    ├── urls.py
    │    └── wsgi.py
    ├── todocreate
    │    ├── __init__.py
    │    ├── __pycache__
    │    ├── admin.py
    │    ├── apps.py
    │    ├── migrations
    │    ├── models.py
    │    ├── tests.py
    │    ├── urls.py
    │    └── views.py
    ├── tododelete
    │    ├── __init__.py
    │    ├── __pycache__
    │    ├── admin.py
    │    ├── apps.py
    │    ├── migrations
    │    ├── models.py
    │    ├── tests.py
    │    ├── urls.py
    │    └── views.py
    ├── tododetail
    │    ├── __init__.py
    │    ├── __pycache__
    │    ├── admin.py
    │    ├── apps.py
    │    ├── migrations
    │    ├── models.py
    │    ├── tests.py
    │    ├── urls.py
    │    └── views.py
    ├── todolist
    │    ├── __init__.py
    │    ├── __pycache__
    │    ├── admin.py
    │    ├── apps.py
    │    ├── migrations
    │    ├── models.py
    │    ├── tests.py
    │    ├── urls.py
    │    └── views.py
    ├── todoupdate
    │    ├── __init__.py
    │    ├── __pycache__
    │    ├── admin.py
    │    ├── apps.py
    │    ├── migrations
    │    ├── models.py
    │    ├── tests.py
    │    ├── urls.py
    │    └── views.py
    └── venv
    ├── bin
    ├── include
    ├── lib
    ├── lib64 -> lib
    └── pyvenv.cfg

todo_project配下のファイル編集

todo_projectのsettings.pyを編集

  • プロジェクト(todo_todoprojectディレクトリ内)のsettings.pyに以下を追加してプロジェクトに「common,todolist,tododetail,todoupdate,tododelete,todocreate」アプリを追加したことを設定します。
  • settings.pyを編集し、33行目あたりにある「INSTALLED_APPS」の箇所に「’common’,’todolist’,’tododetail’,’todoupdate’,’tododelete’,’todocreate’」を追記します。
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'common',
    'todolist',
    'tododetail',
    'todoupdate',
    'tododelete',
    'todocreate',
]
  • テンプレートhtmlを配置している場所について、BASE_DIRで設定します。
  • settings.pyを編集し、57行目あたりにある「TEMPLATES」の箇所の「’DIRS’」に「[BASE_DIR / ‘templates’]」を記載します。
    TEMPLATES = [
        {
            'BACKEND': 'django.template.backends.django.DjangoTemplates',
            'DIRS': [BASE_DIR / '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',
                ],
            },
        },
    ]

    todo_projectのurls.pyを編集

    • HTTPリクエストオブジェクトのURLパターンに応じたviewの設定を行います。
    • 記載するURLパターンは下記です。
    1. 「http://127.0.0.1:8000/admin」であった場合、djangoの管理者ページを表示する
    2. 「http://127.0.0.1:8000/list/」であった場合、todolist(登録したTodoの一覧)のurls.pyにリクエストを投げる
    3. 「http://127.0.0.1:8000/」であった場合、「http://127.0.0.1:8000/list/top/」にリダイレクトする
    4. 「http://127.0.0.1:8000/detail/」であった場合、tododetail(登録したTodoの詳細)のurls.pyにリクエストを投げる
    5. 「http://127.0.0.1:8000/create/」であった場合、todocreate(Todoの登録)のurls.pyにリクエストを投げる
    6. 「http://127.0.0.1:8000/update/」であった場合、todoupdate(登録したTodoの更新)のurls.pyにリクエストを投げる

    上記の内容をurls.pyに記載します。

    from django.contrib import admin
    from django.urls import path, include
    from django.views.generic.base import RedirectView
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('list/', include('todolist.urls')),
        path('', RedirectView.as_view(url='/list/top/', permanent=True)),
        path('detail/', include('tododetail.urls')),
        path('create/', include('todocreate.urls')),
        path('update/', include('todoupdate.urls')),
    ]

    common配下のファイル編集

    • commonアプリでは、複数の他のアプリケーション間で共有される機能やモデルを保持します。

    commonのmodels.pyを編集

    <models.pyで設定する内容>

    項目 変数名 フィールド オプション 補足
    タイトル title CharField 入力可能文字数:100
    作業内容 description TextField 無し
    優先度 priority CharField 入力可能文字数:10
    優先度:高、通常、低
    デフォルト優先度:通常
    優先度はPriorityクラスで設定
    期限 duedate DateField 無し
    from django.db import models
    
    class Priority(models.TextChoices):
        HIGH = 'danger', '高'
        NORMAL = 'warning', '通常'
        LOW = 'primary', '低'
    
    class TodoModel(models.Model):
        title = models.CharField(max_length=100)
        description = models.TextField()
        priority = models.CharField(
            max_length=10, 
            choices=Priority.choices,
            default=Priority.NORMAL
            )
        duedate = models.DateField()
    
        def __str__(self):
            return self.title

    commonのadmin.pyを編集

    • 管理者ページで管理するモデルを登録します。
    from django.contrib import admin
    from .models import TodoModel
    
    admin.site.register(TodoModel)

    commonのforms.pyを編集

    • Todoの登録/更新ページで使用するフォームの行数/プルダウン/日付を設定します。
    from django import forms
    from .models import TodoModel
    
    class TodoModelForm(forms.ModelForm):
        class Meta:
            model = TodoModel
            fields = ('title', 'description', 'priority', 'duedate')
            widgets = {
                'title': forms.TextInput(attrs={'class': 'form-control'}),
                'description': forms.Textarea(attrs={'class': 'form-control', 'rows': 5}),
                'priority': forms.Select(attrs={'class': 'form-select'}),
                'duedate': forms.DateInput(attrs={'class': 'form-control', 'type': 'date'}),
            }

    todolist配下のファイル編集

    todolistのurls.pyを編集

    • HTTPリクエストオブジェクトのURLパターンに応じたviewの設定を行います。
    • 記載するURLパターンは下記です。「http://127.0.0.1:8000/list/top/」であった場合、todoの一覧ページを表示する

      上記の内容をurls.pyに記載します。

      from django.urls import path, include
      from .views import TodoListTop
      
      urlpatterns = [
          path('top/', TodoListTop.as_view(), name='list')
      ]

      todolistのviews.pyを編集

      • commonアプリのTodoModelを一覧表示するためのビューを定義します。
      • TodoListTopクラスはListViewを継承しています。
      • TodoModelのオブジェクトリストをlist.htmlテンプレートに渡し、登録したTodoを表示するためのビューを作成しています。
      • template_nameには、ビューで使用するテンプレートの名前を指定しています。
      • modelには、表示するオブジェクトのリストを提供するモデルについて指定しています。
      from django.shortcuts import render
      from django.views.generic import ListView
      from common.models import TodoModel
      
      class TodoListTop(ListView):
          template_name = 'list.html'
          model = TodoModel

      tododetail配下のファイル編集

      tododetailのurls.pyの編集

      • HTTPリクエストオブジェクトのURLパターンに応じたviewの設定を行います。
      • 記載するURLパターンは下記です。「http://127.0.0.1:8000/detail/(プライマリキー)/」であった場合、プライマリキーに該当するtodoの詳細ページを表示する

      上記の内容をurls.pyに記載します。

      from django.urls import path, include
      from .views import TodoDetail
      
      urlpatterns = [
          path('<int:pk>/', TodoDetail.as_view(), name='detail')
      ]
      • 「<int:pk>/」URLからキャプチャされた整数値をpkという名前のキーワード引数としてビューに渡します。
      • 通常、pk(プライマリキー)はデータベースのレコードを一意に識別するために使います。
      • pk(プライマリキー)には、/1/や/2/の数字が入ります。
      • それぞれの数字はレコードのIDとして扱います。

        tododetailのviews.pyの編集

        • commonアプリのTodoModelを一覧表示するためのビューを定義します。
        • TodoDetailクラスはDetailViewを継承しています。
        • TodoModelのオブジェクトリストをdetail.htmlテンプレートに渡し、登録したTodoの詳細を表示するためのビューを作成しています。
        • template_nameには、ビューで使用されるテンプレートの名前を指定しています。
        • modelには、表示するオブジェクトのリストを提供するモデルについて指定しています。
        from django.shortcuts import render
        from django.views.generic import DetailView
        from common.models import TodoModel
        
        class TodoDetail(DetailView):
            template_name = 'detail.html'
            model = TodoModel

        todoupdate配下のファイル編集

        todoupdateのurls.pyの編集

        • HTTPリクエストオブジェクトのURLパターンに応じたviewの設定を行います。
        • 記載するURLパターンは下記です。「http://127.0.0.1:8000/update/(プライマリキー)/」であった場合、プライマリキーに該当するtodoの更新ページを表示する

        上記の内容をurls.pyに記載します。

        from django.urls import path, include
        from .views import TodoUpdate
        
        urlpatterns = [
            path('<int:pk>/', TodoUpdate.as_view(), name='update')
        ]
        • 「<int:pk>/」URLからキャプチャされた整数値をpkという名前のキーワード引数としてビューに渡します。
        • 通常、pk(プライマリキー)はデータベースのレコードを一意に識別するために使います。
        • pk(プライマリキー)には、/1/や/2/の数字が入ります。
        • それぞれの数字はレコードのIDとして扱います。

        todoupdateのviews.pyの編集

        • commonアプリのTodoModelを一覧表示するためのビューを定義します。
        • TodoUpdateクラスはUpdateViewを継承しています。
        • TodoModelのオブジェクトリストをupdate.htmlテンプレートに渡し、登録したTodoの詳細を表示するためのビューを作成しています。
        • template_nameには、ビューで使用されるテンプレートの名前を指定しています。
        • modelには、表示するオブジェクトのリストを提供するモデルについて指定しています。
        • form_classには、入力フォーム設定の読み込み先を指定します。
        • success_urlには、「更新」に成功したら表示するページを指定しています。
        from django.shortcuts import render
        from django.views.generic import UpdateView
        from common.forms import TodoModelForm
        from common.models import TodoModel
        from django.urls import reverse_lazy
        
        class TodoUpdate(UpdateView):
            template_name = 'update.html'
            model = TodoModel
            form_class = TodoModelForm
            success_url = reverse_lazy('list')

        tododelete配下のファイル編集

        tododeleteのurls.pyの編集

        • HTTPリクエストオブジェクトのURLパターンに応じたviewの設定を行います。
        • 記載するURLパターンは下記です。「http://127.0.0.1:8000/delete/(プライマリキー)/」であった場合、プライマリキーに該当するtodoを削除する

        上記の内容をurls.pyに記載します。

        from django.urls import path, include
        from .views import TodoDelete
        
        urlpatterns = [
            path('<int:pk>/', TodoDelete.as_view(), name='delete')
        ]
        • 「<int:pk>/」URLからキャプチャされた整数値をpkという名前のキーワード引数としてビューに渡します。
        • 通常、pk(プライマリキー)はデータベースのレコードを一意に識別するために使います。
        • pk(プライマリキー)には、/1/や/2/の数字が入ります。
        • それぞれの数字はレコードのIDとして扱います。

        tododeleteのviews.pyの編集

        • commonアプリのTodoModelを一覧表示するためのビューを定義します。
        • TodoDeleteクラスはDeleteViewを継承しています。
        • modelには、削除するオブジェクトのリストを提供するモデルについて指定しています。
        • success_urlには、「削除」に成功したら表示するページを指定しています。
        from django.shortcuts import render
        from django.views.generic import DeleteView
        from common.models import TodoModel
        from django.urls import reverse_lazy
        
        class TodoDelete(DeleteView):
            model = TodoModel
            success_url = reverse_lazy('list')
        

        todocreate配下のファイル編集

        todocreateのurls.pyの編集

        • HTTPリクエストオブジェクトのURLパターンに応じたviewの設定を行います。
        • 記載するURLパターンは下記です。「http://127.0.0.1:8000/create/top/」であった場合、todoの作成ページを表示する

        上記の内容をurls.pyに記載します。

        from django.urls import path, include
        from .views import TodoCreate
        
        urlpatterns = [
            path('top/', TodoCreate.as_view(), name='create')
        ]

        todoupdateのviews.pyの編集

        • commonアプリのTodoModelを一覧表示するためのビューを定義します。
        • TodoUpdateクラスはUpdateViewを継承しています。
        • TodoModelのオブジェクトリストをupdate.htmlテンプレートに渡し、登録したTodoの詳細を表示するためのビューを作成しています。
        • template_nameには、ビューで使用されるテンプレートの名前を指定しています。
        • modelには、表示するオブジェクトのリストを提供するモデルについて指定しています。
        • form_classには、入力フォーム設定の読み込み先を指定します。
        • success_urlには、「作成」に成功したら表示するページを指定しています。
        from django.shortcuts import render
        from django.views.generic import CreateView
        from common.models import TodoModel
        from common.forms import TodoModelForm
        from django.urls import reverse_lazy
        
        class TodoCreate(CreateView):
            template_name = 'create.html'
            model = TodoModel
            form_class = TodoModelForm
            success_url = reverse_lazy('list')

        templates配下のファイル編集

        base.htmlの編集

        • 全htmlのテンプレートとなるhtmlファイルです。
        • CSSフレームワークとしてbootstrap5.3.0を使用します。
        • base.htmlに記載しているhtml基本構造タグ
          <head>,<body>,<script>
        • base.htmlに記載しているdjangoテンプレートタグ
          {% block header %}、 {% block content %}
        <!doctype html>
        <html lang="en">
          <head>
            <meta charset="utf-8">
            <meta name="viewport" content="width=device-width, initial-scale=1">
            <title>Todo 管理</title>
            <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous">
          </head>
          <body>
            <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-geWF76RCwLtnZ8qwWowPQNguL3RmwHVBC9FhGdlKrxdiJJigb/j/68SIy3Te4Bkz" crossorigin="anonymous"></script>
            
            <div class="bg-body-tertiary p-3 p-sm-3 mb-2">
              <div class="container">
                <h2 class="display-4">{% block header %}{% endblock header %}</h2>
              </div>
            </div>
        
            {% block content %}
            {% endblock content %}
            
            <!-- 共通の削除モーダル -->
            <div class="modal fade" id="deleteModal" tabindex="-1" aria-labelledby="deleteModalLabel" aria-hidden="true">
              <div class="modal-dialog">
                <div class="modal-content">
                  <div class="modal-header">
                    <h5 class="modal-title" id="deleteModalLabel">削除の確認</h5>
                    <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
                  </div>
                  <div class="modal-body">
                    <p id="deleteItemName">このアイテムを本当に削除しますか?</p>
                  </div>
                  <div class="modal-footer">
                    <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">キャンセル</button>
                    <button type="button" class="btn btn-danger" id="deleteConfirmButton">削除</button>
                  </div>
                </div>
              </div>
            </div>
        
            <!-- 共通のJavaScript -->
            <script>
              function setupDeleteModal(itemId, itemName) {
                document.getElementById('deleteItemName').textContent = '「' + itemName + '」を本当に削除しますか?';
                var deleteButton = document.getElementById('deleteConfirmButton');
                deleteButton.onclick = function() {
                  document.getElementById('deleteForm' + itemId).submit();
                };
              }
            </script>
        
          </body>
        </html>

        list.htmlの編集

        • djangoテンプレート構造の解説
        タグ 解説
        {% extends ‘base.html’ %} base.htmlを継承する。
        {% block header %}…{% endblock header %} base.html 内の header ブロックを上書きする。ページのタイトルとして “Todo 一覧” を設定している。
        {% block content %}…{% endblock content %} テンプレートのメインコンテンツ部分です。base.html の content ブロックを上書きする。
        • 動的コンテンツ表示の解説
        タグ 解説
        {% for item in object_list %}…{% endfor %} object_listはビューから渡されるmodelのオブジェクト。
        forループで、object_listの各オブジェクトに対して反復処理を行う。ここでは、Todoの各項目を表示する。
        <div class=”alert alert-{{ item.priority }}”…> 各Todoの優先度に応じて背景色を変える。
        {{ item.title }} 各Todoのタイトル。
        {{ item.duedate|date:”o/n/j” }} 各Todoの期日。|date:”o/n/j” は日付を特定の形式で表示するためのフィルタ。
        • ボタン・フォームの解説
        タグ 解説
        <a class=”btn btn-warning mb-2″…> “作成”ラベルのボタンを表示する。
        <a class=”btn btn-primary”…> “詳細”ラベルのボタンを表示する。
        href=”{% url ‘detail’ item.pk %}” ‘detail’はtododetailのurls.pyのnameで設定した値。
        item.pk は対象となる詳細のプライマリキー。
        <a class=”btn btn-success”…> “編集”ラベルのボタンを表示する。
        <button class=”btn btn-secondary”…> “削除”ラベルのボタンを表示する。JavaScript関数 setupDeleteModal を呼び出し、削除を確認するモーダルウィンドウを表示する。
        <form id=”deleteForm{{ item.pk }}”…> 削除操作を行うための隠しフォーム。実際の削除は、このフォームをサブミットすることで行わる。
        {% extends 'base.html' %}
        
        {% block header %}Todo 一覧{% endblock header %}
        
        {% block content %}
        <div class="container">
          <a class="btn btn-warning mb-2" href="{% url 'create' %}" role="button">作成</a>
          {% for item in object_list %}
          <div class="alert alert-{{ item.priority }}" role="alert">
            <p>{{ item.title }}&nbsp;【期日:{{ item.duedate|date:"o/n/j" }}】</p>
            <a class="btn btn-primary" href="{% url 'detail' item.pk %}" role="button">詳細</a>
            <a class="btn btn-success" href="{% url 'update' item.pk %}" role="button">編集</a>
            <button class="btn btn-secondary" onclick="setupDeleteModal({{ item.pk }}, '{{ item.title }}')" data-bs-toggle="modal" data-bs-target="#deleteModal">削除</button>
            <form id="deleteForm{{ item.pk }}" action="{% url 'delete' item.pk %}" method="POST" style="display: none;">
              {% csrf_token %}
            </form>
          </div>
          {% endfor %}
        </div>
        
        {% endblock content %}

        detail.htmlの編集

        • djangoテンプレート構造の解説
        タグ 解説
        {% extends ‘base.html’ %} base.htmlを継承する。
        {% block header %}…{% endblock header %} base.html 内の header ブロックを上書きする。ページのタイトルとして “Todo 詳細” を設定している。
        {% block content %}…{% endblock content %} テンプレートのメインコンテンツ部分です。base.html の content ブロックを上書きする。
        • 動的コンテンツ表示の解説
        タグ 解説
        {{ object.title }} objectはビューから渡されるmodelのオブジェクト。
        Todoのタイトル。
        {{ object.description|linebreaksbr }} Todoの説明。「|linebreaksbr」は改行を <br> タグに変換するフィルタ。
        {% if %}…{% endif %} 条件に基づいて異なる内容を表示する。Todoの優先度に応じて「高」「通常」「低」と表示する。
        {{ item.duedate|date:”o/n/j” }} 各Todoの期日。|date:”o/n/j” は日付を特定の形式で表示するためのフィルタ。
        • ボタン・フォームの解説
        タグ 解説
        <a class=”btn btn-info”…> “Todo一覧”ラベルのボタンを表示する。
        <a class=”btn btn-success”…> “編集”ラベルのボタンを表示する。
        <button class=”btn btn-secondary”…> “削除”ラベルのボタンを表示する。JavaScript関数 setupDeleteModal を呼び出し、削除を確認するモーダルウィンドウを表示する。
        <form id=”deleteForm{{ item.pk }}”…> 削除操作を行うための隠しフォーム。実際の削除は、このフォームをサブミットすることで行わる。
        {% extends 'base.html' %}
        
        {% block header %}Todo 詳細{% endblock header %}
        
        {% block content %}
        <div class="container">
          <table class="table table-striped-columns">
            <tr>
              <th>タイトル</th>
              <td>{{ object.title }}</td>
            </tr>
            <tr>
              <th>作業内容</th>
              <td>{{ object.description|linebreaksbr }}</td>
            </tr>
            <tr>
              <th>優先度</th>
              <td>
                {% if object.priority == 'danger' %}
                  高
                {% elif object.priority == 'warning' %}
                  通常
                {% elif object.priority == 'primary' %}
                  低
                {% else %}
                  {{ object.priority }}
                {% endif %}
              </td>
            </tr>
            <tr>
              <th>期限</th>
              <td>{{ object.duedate|date:"o/n/j" }}</td>
            </tr>
          </table>
          <a class="btn btn-success" href="{% url 'update' object.pk %}" role="button">編集</a>
          <button class="btn btn-secondary" onclick="setupDeleteModal({{ object.pk }}, '{{ object.title }}')" data-bs-toggle="modal" data-bs-target="#deleteModal">削除</button>
          <form id="deleteForm{{ object.pk }}" action="{% url 'delete' object.pk %}" method="POST" style="display: none;">
            {% csrf_token %}
          </form>
          <a class="btn btn-info" href="{% url 'list' %}" role="button">Todo一覧</a>
        </div>
        
        {% endblock content %}

        _form.htmlの編集

        • create.htmlとupdate.htmlで表示する入力フォームのテンプレート
        • 動的コンテンツ表示の解説
          タグ 解説
          {{ form.title }} Todoのタイトル。
          {{ form.description }} Todoの説明。
          {{ form.priority }} Todoの優先度。
          {{ form.duedate }} Todoの期限。
          {{ submit_button_label }} 「作成」「更新」のいずれかが入る。
          入る内容は「_form.html」の呼び出し元で制御している。
          • ボタン・フォームの解説
          タグ 解説
          <button type=”submit” class=”btn btn-warning”>{{ submit_button_label }}</button> 「作成」あるいは「削除」のボタンを表示する。
          <label for=”id_title” …</label> Todoのタイトル入力フォーム。
          <label for=”id_description” …</label> Todoの作業内容入力フォーム。
          <label for=”id_priority” …</label> Todoの優先度入力フォーム。
          <label for=”id_duedate” …</label> Todoの期日入力フォーム。
          <form action="" method="POST" class="row g-3">
            {% csrf_token %}
            <div class="col-12 mb-2">
              <label for="id_title" class="form-label">タイトル</label>
              {{ form.title }}
            </div>
            <div class="col-12 mb-2">
              <label for="id_description" class="form-label">作業内容</label>
              {{ form.description }}
            </div>
            <div class="col-12 mb-2">
              <label for="id_priority" class="form-label">優先度</label>
              {{ form.priority }}
            </div>
            <div class="col-12 mb-2">
              <label for="id_duedate" class="form-label">期日</label>
              {{ form.duedate }}
            </div>
            <div class="col-12">
              <button type="submit" class="btn btn-warning">{{ submit_button_label }}</button>
              <a class="btn btn-info" href="{% url 'list' %}" role="button">Todo一覧</a>
            </div>
          </form>

          create.htmlの編集

          • djangoテンプレート構造の解説
          タグ 解説
          {% extends ‘base.html’ %} base.htmlを継承する。
          {% block header %}…{% endblock header %} base.html 内の header ブロックを上書きする。ページのタイトルとして “Todo 作成” を設定している。
          {% block content %}…{% endblock content %} テンプレートのメインコンテンツ部分。base.html の content ブロックを上書きする。
          {% include ‘_form.html’ with submit_button_label=’作成’ %} _form.htmlを読み込む際、submit_button_labelを”作成”に設定している。
          {% block header %}Todo 作成{% endblock header %}
          
          {% block content %}
          <div class="container">
            {% include '_form.html' with submit_button_label='作成' %}
          </div>
          {% endblock content %}

          update.htmlの編集

          • djangoテンプレート構造の解説
          タグ 解説
          {% extends ‘base.html’ %} base.htmlを継承する。
          {% block header %}…{% endblock header %} base.html 内の header ブロックを上書きする。ページのタイトルとして “Todo 編集” を設定している。
          {% block content %}…{% endblock content %} テンプレートのメインコンテンツ部分。base.html の content ブロックを上書きする。
          {% include ‘_form.html’ with submit_button_label=’作成’ %} _form.htmlを読み込む際、submit_button_labelを”更新”に設定している。
          {% extends 'base.html' %}
          
          {% block header %}Todo 編集{% endblock header %}
          
          {% block content %}
          <div class="container">
            {% include '_form.html' with submit_button_label='更新' %}
          </div>
          {% endblock content %}

          Todoアプリの起動

          • Todoアプリを起動する前に、commonで設定したmodels.pyの内容でデータベースを構築します。
          • 下記の実行後、models.pyの変更がなければ、次回からは実行不要です。
          (venv)$ python manage.py makemigrations
          (venv)$ python manage.py migrate
          • Todoアプリを起動します。
          (venv)$ python manage.py runserver
          • 下記のURLにアクセスすると、Todoアプリに接続できます。
            「http://127.0.0.1:8000/」

          以上でTodoアプリの構築は完了です。

          コメント

          タイトルとURLをコピーしました