Django初心者が簡単なアプリを作る

Django初心者が簡単なアプリ（Todoアプリ）を作りました。
はやたすさんの記事を参考にしました。

https://tech-diary.net/django-todo-tutorial/

この記事では、Pythonのインストール、仮想環境の構築、Djangoのインストール、アプリ作成の流れを書きます。
実行環境：MacBook Air M1, 2020

# Pythonのインストール
以下のサイトにアクセスし、Latest Python 3 Release - Python 3.12.1をクリックします。
https://www.python.org/downloads/macos/
![スクリーンショット 2023-12-09 1.58.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3569835/a99c13f0-63a0-26d8-d755-ddbd24cd2ea4.png)

開いたページを下にスクロールし、macOS 64-bit universal2 installerをクリックします。

![スクリーンショット 2023-12-09 1.59.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3569835/de2e9e8c-97fe-df1b-bf5b-7202e5691a42.png)

インストーラをダウンロードしたら、インストールを進めます。

![スクリーンショット 2023-12-09 2.00.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3569835/51bc1c2e-fbba-a468-09ac-b8722f983a3f.png)

ターミナルでPythonのバージョンを確認します。
```zsh
% python3 -V
Python 3.12.1
```
Python 3.12.1をインストールすることができました。

# 仮想環境の構築
今回は、仮想環境を構築し、アプリ作成を行います。
ターミナルで以下のコマンドを実行します。
```zsh
% mkdir django-todo
% cd django-todo
% python3 -m venv .venv
% source .venv/bin/activate
```
django-todoディレクトリを作成し、仮想環境の構築と起動を行いました。
次回からは、以下のコマンドのみを実行します。
```zsh
% cd django-todo
% source .venv/bin/activate
```

# Djangoのインストール
ターミナルで以下のコマンドを実行します。
```zsh
% python -m pip install Django
% python -m django --version
5.0
```

# アプリ作成
## Djangoプロジェクトの作成
ターミナルで以下のコマンドを実行します。
```zsh
% django-admin startproject config .
```
プロジェクトが動作するか確認します。
```zsh
% python manage.py runserver
```
[http://127.0.0.1:8000/](http://127.0.0.1:8000/)にアクセスすると、以下の画面が表示されます。

![スクリーンショット 2023-12-23 22.52.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3569835/6d4e30d8-37a5-9659-c4fe-1f0898b042f9.png)

## Todoアプリの作成
ターミナルで以下のコマンドを実行します。
```zsh
% python manage.py startapp todo
```

config/settings.pyを編集し、初期設定を行います。
```config/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' # 追加
]

# ...

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

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

以下のコマンドを実行します。
```zsh
python manage.py runserver
```
[http://127.0.0.1:8000/](http://127.0.0.1:8000/)にアクセスすると、以下の画面が表示されます。

![スクリーンショット 2023-12-23 22.52.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3569835/269005f5-6915-ddff-8f17-9401d8ce1087.png)

英語から日本語に変わりました。

## Hello Worldの表示
config/urls.pyを編集します。
```config/urls.py
from django.contrib import admin
from django.urls import path, include # 追加


urlpatterns = [
    path('admin/', admin.site.urls),
    path("", include("todo.urls")) # 追加
]
```

todo/views.pyを編集します。
```todo/views.py
from django.shortcuts import render
from django.http import HttpResponse


def index(request):
    return HttpResponse("<h1>Hello World</h1>")
```

todo/urls.pyを作成します。
```todo/urls.py
from django.urls import path
from . import views


urlpatterns = [
    path("", views.index),
]
```

サーバーを起動します。
```zsh
% python manage.py runserver
```
[http://127.0.0.1:8000/](http://127.0.0.1:8000/)にアクセスすると、以下の画面が表示されます。

![スクリーンショット 2023-12-23 23.09.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3569835/c361464c-3b4d-fac3-df4e-bc4cc638db7b.png)

Hello Worldが表示されました。

## モデルの作成と有効化
todo/models.pyを編集します。
```todo/models.py
from django.db import models


class Todo(models.Model):
    title = models.CharField("タスク名", max_length=30)
    description = models.TextField("詳細", blank=True)
    deadline = models.DateField("締切")

    def __str__(self):
        return self.title
```
ターミナルで以下のコマンドを実行します。
```zsh
% python manage.py makemigrations
% python manage.py migrate
```

## 管理ユーザーの作成
ターミナルで以下のコマンドを実行します。
```zsh
% python manage.py createsuperuser
```
ユーザー名、メールアドレス、パスワードを入力します。
メールアドレスは必要ないようです。
以下のコマンドを実行し、サーバーを起動します。
```zsh
% python manage.py runserver
```
[http://127.0.0.1:8000/admin](http://127.0.0.1:8000/admin)にアクセスすると、以下の画面が表示されます。
![スクリーンショット 2023-12-23 23.15.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3569835/f52b852b-018c-92a0-88d7-65f6b799b136.png)

ユーザー名とパスワードを入力し、ログインすると、以下の画面が表示されます。

![スクリーンショット 2023-12-23 23.17.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3569835/78e6f561-a00c-87ea-7d57-d9714c0e424f.png)

todo/admin.pyを編集します。
```todo/admin.py
from django.contrib import admin
from .models import Todo


admin.site.register(Todo)
```
以下のコマンドを実行し、サーバーを起動します。
```zsh
% python manage.py runserver
```
[http://127.0.0.1:8000/admin](http://127.0.0.1:8000/admin)にアクセスします。

![スクリーンショット 2023-12-23 23.20.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3569835/c1b0f00c-c71b-4cfd-b902-fd7db467f95a.png)

Todoテーブルが表示されました。
Todoを追加します。

![スクリーンショット 2023-12-24 0.35.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3569835/25527c02-74d5-2717-e274-28f03097ce5c.png)

## Todoの表示
todo/views.pyを編集します。
```todo/views.py
from django.shortcuts import render
from django.views.generic import ListView

from .models import Todo


class TodoList(ListView):
    model = Todo
    context_object_name = "tasks"
```

todo/urls.pyを編集します。
```todo/urls.py
from django.urls import path
from .views import TodoList

urlpatterns = [
    path("", TodoList.as_view(), name="list"),
]
```

todo/templates/todo/todo_list.htmlを作成します。
```todo/templates/todo/todo_list.html
<h1>Todo List</h1>

<table>
    <thead>
        <tr>
        <th scope="col">title</th>
        <th scope="col">deadline</th>
        </tr>
    </thead>
    <tbody>
    {% for task in tasks %}
    <tr>
        <td>{{ task.title }}</td>
        <td>{{ task.deadline }}</td>
    </tr>
    {% endfor %}
    </tbody>
</table>
```
サーバーを起動し、[http://127.0.0.1:8000/](http://127.0.0.1:8000/)にアクセスすると、以下の画面が表示されます。

![スクリーンショット 2023-12-24 0.48.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3569835/5ce666b5-b756-b887-c865-d35274c318a0.png)

Todoアプリを起動することができ、作成したTodoを表示することができました。

## Todoの詳細の表示
todo/views.pyを編集します。
```todo/views.py
from django.shortcuts import render
from django.views.generic import ListView, DetailView

from .models import Todo


class TodoList(ListView):
    model = Todo
    context_object_name = "tasks"


class TodoDetail(DetailView):
    model = Todo
    context_object_name = "task"
```
todo/urls.pyを編集します。
```todo/urls.py
from django.urls import path
from .views import TodoDetail, TodoList # 追加

urlpatterns = [
    path("", TodoList.as_view(), name="list"),
    path("detail/<int:pk>", TodoDetail.as_view(), name="detail"), # 追加
]
```
todo/templates/todo/todo_list.htmlを編集します。
```todo/templates/todo/todo_list.html
<h1>Todo List</h1>

<table>
    <thead>
        <tr>
        <th scope="col">title</th>
        <th scope="col">deadline</th>
        </tr>
    </thead>
    <tbody>
    {% for task in tasks %}
    <tr>
        <td>{{ task.title }}</td>
        <td>{{ task.deadline }}</td>
        <td><a href="{% url 'detail' task.pk %}">詳細</a></td>
    </tr>
    {% endfor %}
    </tbody>
</table>
```

todo/templates/todo/todo_detail.htmlを作成します。
```todo/templates/todo/todo_detail.html
<h1>Todo Detail</h1>

<div>
    <h2>{{task.title}}</h2>
    <p>{{task.deadline}}</p>
    <p>{{task.description}}</p>
</div>
```
サーバーを起動し、[http://127.0.0.1:8000/](http://127.0.0.1:8000/)にアクセスすると、以下の画面が表示されます。

![スクリーンショット 2023-12-24 0.54.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3569835/1b8a28d9-6c77-d8e0-31ba-0b56f4c25faa.png)

詳細をクリックすると、以下の画面が表示されます。

![スクリーンショット 2023-12-24 0.54.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3569835/b8ffe054-c615-9a16-4a92-38df6049aa1b.png)

Todoの詳細を表示することができました。

## Todoの作成
todo/views.pyを編集します。
```todo/views.py
from django.shortcuts import render
from django.views.generic import ListView, DetailView, CreateView

from django.urls import reverse_lazy

from .models import Todo

# ...

class TodoCreate(CreateView):
    model = Todo
    fields = "__all__"
    success_url = reverse_lazy("list")
```
todo/urls.pyを編集します。
```todo/urls.py
from django.urls import path
from .views import TodoDetail, TodoList, TodoCreate # 追加

urlpatterns = [
    path("", TodoList.as_view(), name="list"),
    path("detail/<int:pk>", TodoDetail.as_view(), name="detail"),
    path("create/", TodoCreate.as_view(), name="create"),　# 追加
]
```
todo/templates/todo/todo_list.htmlを編集します。
```todo/templates/todo/todo_list.html
<h1>Todo List</h1>

<p><a href="{% url 'create' %}">新規作成</a></p>

<!-- ... -->
```
todo/templates/todo/todo_form.htmlを作成します。
```todo/templates/todo/todo_form.html
<form action="" method="POST">{% csrf_token %}
    <table>
        {{ form.as_table }}
    </table>
    <input type="submit" value="送信">
</form>
```
サーバーを起動し、[http://127.0.0.1:8000/](http://127.0.0.1:8000/)にアクセスすると、以下の画面が表示されます。

![スクリーンショット 2023-12-24 1.01.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3569835/19da592d-7f9c-390f-0236-0554a1d7965a.png)

新規作成をクリックし、Todoを作成します。

![スクリーンショット 2023-12-24 1.04.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3569835/4533915a-58a1-48c8-42e4-dc8a64874308.png)

送信をクリックすると、以下の画面が表示されます。

![スクリーンショット 2023-12-24 1.04.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3569835/c0420cbb-3b03-832e-3741-d802a399ea4e.png)

Todoを作成することができました。

## Todoの編集
todo/views.pyを編集します。
```todo/views.py
from django.shortcuts import render
from django.views.generic import ListView, DetailView, CreateView, UpdateView

from django.urls import reverse_lazy

from .models import Todo

# ...

class TodoUpdate(UpdateView):
    model = Todo
    fields = "__all__"
    success_url = reverse_lazy("list")
```
todo/urls.pyを編集します。
```todo/urls.py
from django.urls import path
from .views import TodoDetail, TodoList, TodoCreate, TodoUpdate

urlpatterns = [
    path("", TodoList.as_view(), name="list"),
    path("detail/<int:pk>", TodoDetail.as_view(), name="detail"),
    path("create/", TodoCreate.as_view(), name="create"),
    path("update/<int:pk>", TodoUpdate.as_view(), name="update"),
]
```
todo/templates/todo/todo_list.htmlを編集します。
```todo/templates/todo/todo_list.html
<h1>Todo List</h1>

<p><a href="{% url 'create' %}">新規作成</a></p>

<table>
    <thead>
        <tr>
        <th scope="col">title</th>
        <th scope="col">deadline</th>
        </tr>
    </thead>
    <tbody>
    {% for task in tasks %}
    <tr>
        <td>{{ task.title }}</td>
        <td>{{ task.deadline }}</td>
        <td><a href="{% url 'detail' task.pk %}">詳細</a></td>
        <td><a href="{% url 'update' task.pk %}">編集</a></td>
    </tr>
    {% endfor %}
    </tbody>
</table>
```
サーバーを起動し、[http://127.0.0.1:8000/](http://127.0.0.1:8000/)にアクセスすると、以下の画面が表示されます。

![スクリーンショット 2023-12-24 1.09.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3569835/ba1e286e-f919-6105-1d3d-c25e300c7624.png)

Todoを編集することができるようになりました。

## Todoの削除
todo/views.pyを編集します。
```todo/views.py
from django.shortcuts import render
from django.views.generic import ListView, DetailView, CreateView, UpdateView, DeleteView

from django.urls import reverse_lazy

from .models import Todo

# ...

class TodoDelete(DeleteView):
    model = Todo
    context_object_name = "task"
    success_url = reverse_lazy("list")
```
todo/urls.pyを編集します。
```todo/urls.py
from django.urls import path
from .views import TodoDetail, TodoList, TodoCreate, TodoUpdate, TodoDelete

urlpatterns = [
    path("", TodoList.as_view(), name="list"),
    path("detail/<int:pk>", TodoDetail.as_view(), name="detail"),
    path("create/", TodoCreate.as_view(), name="create"),
    path("update/<int:pk>", TodoUpdate.as_view(), name="update"),
    path("delete/<int:pk>", TodoDelete.as_view(), name="delete"),
]
```
todo/templates/todo/todo_list.htmlを編集します。
```todo/templates/todo/todo_list.html
<h1>Todo List</h1>

<p><a href="{% url 'create' %}">新規作成</a></p>

<table>
    <thead>
        <tr>
        <th scope="col">title</th>
        <th scope="col">deadline</th>
        </tr>
    </thead>
    <tbody>
    {% for task in tasks %}
    <tr>
        <td>{{ task.title }}</td>
        <td>{{ task.deadline }}</td>
        <td><a href="{% url 'detail' task.pk %}">詳細</a></td>
        <td><a href="{% url 'update' task.pk %}">編集</a></td>
        <td><a href="{% url 'delete' task.pk %}">削除</a></td>
    </tr>
    {% endfor %}
    </tbody>
</table>
```
todo/templates/todo/todo_confirm_delete.htmlを作成します。
```todo/templates/todo/todo_confirm_delete.html
<h1>タスクの削除</h1>
<form action="" method="POST">{% csrf_token %}
    <p>{{task}}</p>
    <input type="submit" value="delete">
</form>
```

サーバーを起動し、[http://127.0.0.1:8000/](http://127.0.0.1:8000/)にアクセスすると、以下の画面が表示されます。

![スクリーンショット 2023-12-24 1.12.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3569835/92d5f863-3236-1ed7-cf7b-097d6f1a5b3f.png)

削除をクリックすると、以下の画面が表示されます。

![スクリーンショット 2023-12-24 1.12.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3569835/05e9c7ac-127a-348a-83cb-4a289738af9b.png)

deleteをクリックします。

![スクリーンショット 2023-12-24 15.07.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3569835/c09ba3b2-8810-32b2-b2b2-9a885d6c40a2.png)

Todoが削除されました。

[はやたすさんの記事](https://tech-diary.net/django-todo-tutorial/)を参考にしながら、DjangoでTodoアプリを作ることができました。
次は、読書メモアプリを作りたいです。

# 参考文献
https://docs.djangoproject.com/ja/5.0/topics/install/
https://tech-diary.net/django-todo-tutorial/
https://qiita.com/white1107/items/5063d38cd5474b412e3e