【Django】高速でTODOアプリを作成する

今回はDjangoフレームワークを使って高速でTODOアプリを作成したいと思います。上の図のようにTODOリストをカテゴリー分けを行える内容になります。
残念ながらカテゴリー・TODOリストの作成は既存のDjango管理画面にて行う事になりますが、削除機能は一覧画面にて行えるようにしています。
もし仮想環境下での作業をご希望の方がいましたら、こちらの記事を参照ください。
| 実行環境 | 
|---|
| Windows Subsystem for Linux | 
| Python 3.6.8 | 
| pip 9.0.1 | 
| 使用ライブラリ | ライセンス | 
|---|---|
| Django==2.2.6 | BSD | 
Djangoのインストールが終わったらプロジェクトの作成をします。
$ django-admin startproject project
projectディレクトリに移動し、アプリケーションを作成します。
$ cd project
/project$ python3 manage.py startapp todo
アプリが作成されましたらprojectディレクトリ内のsettings.pyを編集します。
# /project/settings.py
INSTALLED_APPS = [
    'todo.apps.TodoConfig',  # ←アプリを追加
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]
....
....
LANGUAGE_CODE = 'ja'  # ←編集
TIME_ZONE = 'Asia/Tokyo'  # ←編集
あと一回settings.pyに訪れますが、この時点でモデルを作成してしまいます。
アプリディレクトリ内のmodels.pyにてデータベースであるテーブルを作成します。
# /todo/models.py
from django.db import models
"""カテゴリー"""
class Category(models.Model):
    title = models.CharField('タイトル', max_length=20)
    """self.titleとすることでadmin管理画面にてインスタンス変数として表示される"""
    def __str__(self):
        return self.title
"""
タイトル、日付テーブルとカテゴリーを紐づけるためのテーブル。
PROTECTは紐づいているデータが存在すれば消されない
"""
class Todo(models.Model):
    title = models.CharField('タイトル', max_length=50)
    created_at = models.DateTimeField('日付', auto_now_add=True)
    category = models.ForeignKey(Category, on_delete=models.PROTECT)
    def __str__(self):
        return self.title
モデルで定義したテーブルをDjango管理画面にて表示させるようにadmin.pyを追記します。
# /todo/admin.py
from django.contrib import admin
from .models import Category, Todo
admin.site.register(Category)
admin.site.register(Todo)
ターミナルにてmanage.pyがあるディレクトリでモデルをデータベースへ適用させます。
/project$ python3 manage.py makemigrations
Migrations for 'todo':
  todo/migrations/0001_initial.py
    - Create model Category
    - Create model Todo
/project$ python3 manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
正しくモデルが定義されていれば上手く適用されます。
続けてDjango管理者の為のクリエイトスーパーユーザーコマンドで管理者登録も行います。
/project$ python3 manage.py createsuperuser
ユーザー名
アドレス
パスワード
確認パス
それではプロジェクトを起動してブラウザにてローカルサーバーを開きます。
/project$ python3 manage.py runserver
'http://127.0.0.1:8000'を開く
127.0.0.1:8000/adminと検索し管理画面を開きCategory、Todoの項目があればひとまず成功です。

カテゴリー、Todoに幾つかデータを保存できたらテンプレートまでを作成していきます。
データベースであるモデルを作成しデータを保存したので、あとは表示させるための記述をしていくだけです。
順番はproject下のurls.pyを編集しアプリ下にてurls.pyを新規作成、views.pyを編集しカテゴリーに使用するPythonファイルを作成し一旦settings.pyに戻って追記したらtemplateを作成して終了です。
project
|
|--project
|       |--urls.py
|       |--settings.py
|
|--todo
      |--urls.py
      |--views.py
      |--category_list.py
      |--templates
                |
                |--todo
                      |--base.html
                      |--index.html
プロジェクト下のurls.pyを編集します。
# /project/urls.py
from django.contrib import admin
from django.urls import path, include  # ←追記
urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('todo.urls')),  # ←追記
]
アプリ下(todo)にてurls.pyを新規作成。
# /todo/urls.py
from django.urls import path
from . import views
app_name = 'todo'
urlpatterns = [
        path('', views.index, name='index'),  # ←一覧
        path('<int:id>/delete/', views.delete, name='delete'),  # ←削除機能用
        path('todo/<str:category>/', views.todo_category, name='todo_category'),  # ←カテゴリ
]
views.pyはURL、モデル、テンプレートへ引き渡しをする架け橋であります。
# /todo/views.py
from django.shortcuts import render, redirect, get_object_or_404
from .models import Todo, Category
"""一覧表示"""
def index(request):
    todo = Todo.objects.order_by('title')
    return render(request, 'todo/index.html', {'todo': todo})
"""削除機能"""
def delete(request, id):
    todo = get_object_or_404(Todo,pk=id)
    todo.delete()
    return redirect('todo:index')
"""カテゴリ"""
def todo_category(request, category):
    category = Category.objects.get(title=category)
    """カテゴリで絞り込む"""
    todo = Todo.objects.filter(category=category).order_by('title')
    return render(request, 'todo/index.html', {'todo': todo, 'category': category})
カテゴリのタイトルを取得するために新しくPythonファイルを作成。
# /todo/category_list.py
from .models import Category
def common(request):
    context = {
            'category_list': Category.objects.all(),
    }
    return context
作成したPythonファイルをsettings.pyに読み込ませる。
# /project/settings.py
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        '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.category_list.common',  # ←追記
            ],
        },
    },
]
これで先ほど作成したcategory_list.pyファイル内の「category_list」をDjangoのテンプレートタグにて使用することができる。
あとはtemplatesディレクトリ・todoディレクトリ・base.html・index.htmlを作成するだけです。
templates/todo/にてベースとなるhtmlファイルを作成します。
<!-- /todo/templates/todo/base.html -->
<!doctype html>
<html>
    <head>
        <title>TODO</title>
    </head>
    <body>
        <h2>カテゴリー</h2>
        <ul>
            <a href="{% url 'todo:index' %}">
                <li>トップ</li>
            </a>
            {% for category in category_list %}
                <a href="{% url 'todo:todo_category' category %}">
                    <li>{{ category }}></li>
                </a>
            {% endfor %}
        </ul>
        {% block content %}
        {% endblock %}
    </body>
</html>
同じ配下に子テンプレートとなるindex.htmlを作成する。
<!-- /todo/templates/todo/index.html -->
{% extends 'todo/base.html' %}
{% block content %}
    <h2>TODOリスト</h2>
    {% if category %}
        <h2>カテゴリー: {{ category.title }}</h2>
    {% endif %}
    {% for todos in todo %}
        <ul>
            <li>{{ todos.title }}--{{ todos.created_at }}</li>
            <form method="POST" action="{% url 'todo:delete' todos.pk %}">
                {% csrf_token %}
                <button type="submit">削除</button>
            </form>   
        </ul>
    {% endfor %}
{% endblock %}
カテゴリーで絞り込むこともできます。

高速で終わらすためにも以上をもって終了したいと思います。
最後までご覧いただきありがとうございました。