Djangoで作るToDoリスト

# 概要
Python3とDjango2.1を利用してToDoリストを作成します。
Djangoの汎用クラスビューを利用して、下記の基本的なCRUDの機能を実装します。

* ToDoの一覧表示機能
* ToDoの詳細表示機能
* ToDoの作成機能
* ToDoの編集機能
* ToDoの削除機能

# 開発環境
開発環境は下記を利用しています。
Python 3.6.7
Django 2.1.8
SQLite 3.7.17

# GitHub
サンプルのプログラムのリポジトリは下記のURLになります。
https://github.com/acecrc/django-todo

# Djangoで作るToDoリスト
## 仮想環境の構築
下記のコマンドでpythonの仮想環境を構築してください。

```
$ virtualenv ~/eb-virt
$ source ~/eb-virt/bin/activate
```

## Djangoのインストール
下記のコマンドで仮想環境にDjangoをインストールしてください。

```
(eb-virt)~$ pip3 install django==2.1.8
(eb-virt)~$ pip3 freeze
```

## プロジェクトの作成
下記のコマンドでDjangoのプロジェクトを作成してください。

```
(eb-virt)~$ mkdir django-todo
(eb-virt)~$ cd django-todo
(eb-virt)~$ python3 -m django startproject mysite .
```

## settings.pyの初期設定
Djangoを起動させるための初期設定を行います。
mysite/settings.py を下記のように編集してください。

```mysite/settings.py
# ALLOWED_HOSTS = []
ALLOWED_HOSTS = ['*']

# LANGUAGE_CODE = 'en-us'
LANGUAGE_CODE = 'ja'

# TIME_ZONE = 'UTC'
TIME_ZONE = 'Asia/Tokyo'
```

## todoアプリケーションの作成
todoアプリケーションを作成するため、下記のコマンドを実行してください。

```
$ python3 manage.py startapp todo
```

## todoアプリケーションの組み込み設定
mysite/settings.py を編集してtodoアプリケーションを組み込みます。

```mysite/settings.py
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'todo.apps.TodoConfig', #todoアプリケーションの組み込み
]
```

## todoアプリケーションのモデル作成
今回モデルは「ToDo」「作成日時」「更新日時」のシンプルな物を作成しました。

```todo/models.py
from django.db import models 
 
 
class Todo(models.Model): 
    todo = models.CharField('ToDo', max_length=100, blank=False)
    created_at = models.DateTimeField('作成日時', auto_now_add=True)
    updated_at = models.DateTimeField('更新日時',auto_now=True)
    
    def __str__(self):
        return self.todo
```

## todoアプリケーションのフォームクラス作成
todoアプリケーションのフォームクラスを作成します。今回は入力項目を「ToDo」のみにしたいので、「作成日時」と「更新日時」を入力項目から除外しています。

```todo/forms.py
from django import forms
from .models import Todo

class TodoForm(forms.ModelForm):
    class Meta:
        model = Todo
        exclude = ('created_at','updated_at',)  #入力項目から作成日時、更新日時を除外
```

## todoアプリケーションのビュー作成
ToDoの各機能に対応するようにDjangoの汎用クラスビューを利用してtodoアプリケーションのビューを作成します。

```todo/views.py
from django.views import generic
from django.urls import reverse_lazy
from .models import Todo
from .forms import TodoForm

# ToDoの一覧表示機能
class TodoListView(generic.ListView):
    model = Todo
    paginate_by = 5

# ToDoの詳細表示機能
class TodoDetailView(generic.DetailView):
    model = Todo

# ToDoの作成機能
class TodoCreateView(generic.CreateView):
    model = Todo
    form_class = TodoForm
    success_url = reverse_lazy('todo:list')

# ToDoの編集機能
class TodoUpdateView(generic.UpdateView):
    model = Todo
    form_class = TodoForm
    success_url = reverse_lazy('todo:list')

# ToDoの削除機能
class TodoDeleteView(generic.DeleteView):
    model = Todo
    success_url = reverse_lazy('todo:list')
```

## todoアプリケーションのURLを設定
まず、todo/でブラウザから接続された際、mysiteのURL設定ではなく、todoアプリケーションのURL設定を参照するように記載を行います。

```mysite/urls.py
from django.contrib import admin
from django.urls import include, path   # includeをimport

urlpatterns = [
    path('todo/', include('todo.urls')),    # todo/でアクセスした際はtodoのurl設定を参照させる
    path('admin/', admin.site.urls),
]
```

todoアプリケーションに関するURL設定を下記ののように設定します。

```todo/urls.py
from django.urls import path
from . import views 
 
app_name = 'todo' 

urlpatterns = [
    path('list/', views.TodoListView.as_view(), name='list'), 
    path('detail/<int:pk>/', views.TodoDetailView.as_view(), name='detail'), 
    path('create/', views.TodoCreateView.as_view(), name='create'), 
    path('update/<int:pk>/', views.TodoUpdateView.as_view(), name='update'), 
    path('delete/<int:pk>/', views.TodoDeleteView.as_view(), name='delete'), 
]
```

## todoアプリケーションのテンプレート作成

まずは、テンプレート用のディレクトリを下記のようなに作成してください。

```
(eb-virt) $mkdir -p todo/templates/todo
```

次に、htmlのベースとなるテンプレートを下記のように作成します。

```todo/templates/base.html
{% load static %}
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="utf-8">
</head>
<body>
    {% block content %}
    {% endblock %}
</body>
</html>
```

ToDoの一覧表示機能のためのテンプレートを下記のように作成しました。

```todo/templates/todo/todo_list.html
{% extends 'base.html' %}

{% block content %}
<h1>ToDoの一覧</h1>
<a href="{% url 'todo:create' %}">追加</a>
<table>
  <thead>
    <tr>
      <th scope="col">ID</th>
      <th scope="col">ToDo</th>
      <th scope="col">作成日時</th>
      <th scope="col">更新日時</th>
      <th scope="col">操作</th>
    </tr>
  </thead>
  <tbody>
    {% for item in object_list %}
    <tr>
      <td scope="row"><a href="{% url 'todo:detail' pk=item.id %}">{{ item.id }}</a></td>
      <td>{{ item.todo }}</td>
      <td>{{ item.created_at }}</td>
      <td>{{ item.updated_at }}</td>
      <td>
        <a href="{% url 'todo:update' pk=item.id %}">修正</a>
        <a href="{% url 'todo:delete' pk=item.id %}">削除</a>
      </td>
    </tr>
    {% endfor %}
  </tbody>
</table>
{%endblock%}
```

ToDoの詳細表示機能のためのテンプレートを下記のように作成しました。

```todo/templates/todo/todo_detail.html
{% extends 'base.html' %}

{% block content %}
<h1>ToDoの詳細</h1>
<table>
    <tr>
        <th>ID</th>
        <td>{{ object.id }}</td>
    </tr>
    <tr>
        <th>ToDo</th>
        <td>{{ object.todo }}</td>
    </tr>
    <tr>
        <th>作成日時</th>
        <td>{{ object.created_at }}</td>
    </tr>
    <tr>
        <th>更新日時</th>
        <td>{{ object.updated_at }}</td>
    </tr>
</table>
<a href="{% url 'todo:list' %}">戻る</a>
{%endblock%}
```

ToDoの作成機能とToDoの編集機能のためのテンプレートを下記のように作成しました。

```todo/templates/todo/todo_form.html
{% extends 'base.html' %}

{% block content %}

{% if object %}
<h1>ToDoの編集</h1>
{% else %}
<h1>ToDoの作成</h1>
{% endif %}

<form method="post">
    {% csrf_token %}
    {{ form }} 
    <button type="submit">送信</button>
</form>
<a href="{% url 'todo:list' %}">戻る</a>
{%endblock%}
```

ToDoの削除機能のためのテンプレートを下記のように作成しました。

```todo/templates/todo/todo_confirm_delete.html
{% extends 'base.html' %}

{% block content %}
<h1>ToDoの削除</h1>
<table>
    <tr>
        <th>ID</th>
        <td>{{ object.id }}</td>
    </tr>
    <tr>
        <th>ToDo</th>
        <td>{{ object.todo }}</td>
    </tr>
    <tr>
        <th>作成日時</th>
        <td>{{ object.created_at }}</td>
    </tr>
    <tr>
        <th>更新日時</th>
        <td>{{ object.updated_at }}</td>
    </tr>
</table>
<form action="" method="post">
    {% csrf_token %}
    <input type="submit"value="削除" />
</form>
<a href="{% url 'todo:list' %}">戻る</a>
{%endblock%}
```

## todoアプリケーションのマイグレーション
データベースにtodoアプリケーション用のテーブルを作成するために、下記のコマンドを実行してください。

```
(eb-virt) $ python3 manage.py makemigrations
(eb-virt) $ python3 manage.py migrate
```

## アプリケーションの動作確認
プログラムが完成しましたので、アプリケーションの動作確認を行ってみましょう。
下記のコマンドでDjangoサーバを起動させてください。

```
(eb-virt) $ python3 manage.py runserver 8080
```

ブラウザを起動し、下記のURLにアクセスする。
http://127.0.0.1:8080/todo/list/
成功すると下記のようなアプリケーションが表示されます。
![django-todo_list.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/131520/2516f990-1584-2d8f-c460-a9c64c4d7675.png)


以上