新人エンジニアの、新人エンジニアによる、新人エンジニアのためのToDoアプリ作成(Django/Mac)

## 初めに
初めまして、私は実務経験なし/IT知識皆無の新人エンジニアです。
今回、私は初めてPython/Djangoで1つのアプリを作成しました。

初めてのアプリ作成とのことで、気持ちが高ぶりました！
しかし、その気持ちは一瞬で砕け散りました、、
自身の圧倒的知識不足・経験不足で全く手が動かなかった、、
あの瞬間は、本当に辛かった、、

同じ思いを他の新人エンジニアにさせないために記録を残しておこうと思います！

#### 取り組んだ内容

使用PC：Mac

* Pythonのインストール
* Djangoでの開発環境の準備
* ToDoListの機能を持ったWebアプリケーション開発

## Pythonのインストール
まず初めに開発で扱うPythonのインストールを行いました。
また、Pythonをインストールする為のものを先にインストールする必要もあります。
初心者の私はこの時点で既に大変でした、、

### Homebrewのインストール

まず初めにHomebrewがインストールされているかの確認を行います。

**ターミナルを起動し、以下のコマンドを実行**
~~~r
brew -v
~~~
::: note
表示について
* 「zsh: command not found: brew」と表示された場合
   - Homebrewのインストールがされていない
* 「Homebrew ~.~.~」と表示された場合
    - インストール済みなので、この章は飛ばそう
:::

**次に以下のコマンドをターミナルで実行**
~~~r
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
~~~
:::note warn
注意
途中でPCログイン時に使用するPWを要求されるので、入力してEnterキーを押す
（この際、画面には何も表示されないが正常に入力されている)
また、インストールの途中にも「Enterキー」を押すことが求められます
:::

**M1搭載のMacの場合、さらに以下の二つのコマンドを実行してください**
~~~r
echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> ~/.zshrc
~~~
~~~r
source ~/.zshrc
~~~
**続いて以下のコマンドを実行**
~~~r
brew -v
~~~
「Homebrew ~.~.~」のような文字が表示されればOK！

### pyenvのインストール

こちらもまず初めにpyenvがインストールされているかの確認を行う。

**以下のコマンドを実行**
~~~r
pyenv -v
~~~
:::note
表示について
* 「zsh: command not found: pyenv」と表示された場合
     - Homebrewのインストールがされていない
* 「pyenv ~.~.~」と表示された場合
    - インストール済みなので、この章は飛ばそう
:::

**以下のコマンドを実行**
~~~r
brew install pyenv
~~~
**以下のコマンドを実行**
~~~r
pyenv -v
~~~

「pyenv ~.~.~」のような文字が表示されればOK！

### pyenvの設定

**以下のコマンドを実行すると、pyenvの初期化の設定を.zshrcファイルに書き込むことができる**
~~~r
echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.zshrc
~~~
~~~r
echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.zshrc
~~~
~~~r
echo 'eval "$(pyenv init -)"' >> ~/.zshrc
~~~
**以下のコマンドを実行することで、.zshrcファイルの設定を反映させる**
~~~r
source ~/.zshrc
~~~
:::note warn
注意
これらのコマンドは、実行しても特に何も表示されない
:::

以上で、pyenvの設定完了です。

### xzのインストール
**以下のコマンドを実行**
~~~r
brew install xz
~~~
### Pythonのインストール

インストールするバージョンの確認を初めに行います。

**以下のコマンドを実行**
~~~r
pyenv install --list
~~~
「3.6.5」のような数字から始まる箇所を探す
ここに表示されているのは、現在インストールが可能なPythonのバージョン

**以下のコマンドを実行して、インストール（先程調べた最新のバージョン）**
~~~r
pyenv install 3.13.0a0
~~~
**次に以下のコマンドを実行して、バージョン確認を行う**
~~~r
pyenv versions
~~~
まだこの状態では先程インストールしたバージョンを使用できません。

**以下のコマンドを実行**
~~~r
pyenv global 3.13.0a0
~~~
**以下のコマンドを実行、確認する**
~~~r
python --version
~~~
## Djangoでの開発環境の準備

### Djangoのインストール

アプリケーションを管理するためのディレクトリを作成します。

**先程のディレクトリに移動し、ターミナルで以下のコマンドを実行**
~~~r
pip3 install django
~~~

Djangoがインストールされているかどうか、またどのバージョンかを確認する。

**以下のコマンドをシェルスクリプトで実行**
~~~r
python -m django --version
~~~
以上でWebアプリケーション開発に必要なものを全て用意することができました。
それでは、本題のWebアプリケーションを開発していきましょう！

## ToDoListの機能を持ったWebアプリケーション開発

### プロジェクトを作成する

それでは、いよいよtodoアプリケーションを開発していきます。
今回参照させていただいた記事は[こちら](https://qiita.com/tsubasa2345/items/140e6b95638ad58a1999)


初めてDjangoを使う際は、最初のセットアップを行う必要があります。
通常は、Djangoのプロジェクトを構成するコードが自動生成されます。
（プロジェクトの説明についてはここでは省略）

コマンドラインから、コードを置きたいディレクトリに移動
**以下のコマンドを実行**
~~~r
django-admin startproject mysite
~~~

これにより現在のディレクトリに**mysite**ディレクトリが作成されます。
作成されたものを見てみましょう。

~~~r
mysite/
    manage.py
    mysite/
        __init__.py
        settings.py
        urls.py
        asgi.py
        wsgi.py
~~~
各ファイルの役割についての説明はここでは省略

### 開発用サーバー

Djangoのプロジェクトがうまく動作するか確認しましょう。

**外側のmysiteディレクトリに移動ができたら下記のコマンドを実行**
~~~r
python manage.py runserver
~~~
するとコマンドライン上で下記の出力が確認できます。
~~~r
Performing system checks...

System check identified no issues (0 silenced).

You have unapplied migrations; your app may not work properly until they are applied.
Run 'python manage.py migrate' to apply them.

8月 23, 2023 - 13:00:00
Django version 4.2, using settings 'mysite.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
~~~
:::note
注釈
この段階で警告が出た際は、無視してOK!!
:::

これにより、Django開発サーバが起動できました。
さあ、ブラウザで[django](http://127.0.0.1:8000/)にアクセスしてみてください！

"Congratulations!"と表示された、ロケットが離陸しているページが出たら成功です!

### ToDoListアプリケーションをつくる

さぁ、いよいよアプリを作成していきましょう。
アプリはPythonパス上のどこにでも置くことが可能です。

**任意のディレクトリに移動し、下記のコマンドを実行**
~~~r
python manage.py startapp todo
~~~

このコマンドによって、ToDoディレクトリが作成されます。
全容は以下の通りです。
~~~r
todo/
    __init__.py
    admin.py
    apps.py
    migrations/
        __init__.py
    models.py
    tests.py
    views.py
~~~

### ToDoアプリケーションの組み込み

下記ファイルを編集してToDoアプリを組み込みます。
~~~r:mysite/settings.py
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アプリケーションのモデル作成

今回モデルは「タイトル」,「内容」,「優先度」,「期日」のものを作成しました。

**下記のようにコードを入力した**
~~~r:todo/models.py
from django.db import models

# Create your models here.

CHOICE = (('danger','high'),('warning','normal'),('primary','low'))

class TodoModel(models.Model):
    title = models.CharField(max_length=100)
    contents = models.TextField()
    priority = models.CharField(
        max_length=50,
        choices = CHOICE,
        null =True
     )
    duedate = models.DateField()

#管理画面のobjectの表示をタイトルと一致して表示
    def __str__(self):
        return self.title
~~~

### ToDoアプリケーションのビュー作成

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

**下記のようにコードを入力した**
~~~r:todo/views.py
from django.shortcuts import render
from django.views.generic import ListView,DetailView,CreateView,DeleteView,UpdateView
from .models import TodoModel
from django.urls import reverse_lazy

# ToDoの一覧表示機能
class TodoList(ListView):
    template_name = 'todo/list.html'
    model = TodoModel

# ToDoの詳細表示機能
class TodoDetail(DetailView):
    template_name = 'todo/detail.html'
    model = TodoModel

# ToDoの作成機能
class TodoCreate(CreateView):
    template_name = 'todo/create.html'
    model = TodoModel
    fields = ('title','contents','priority','duedate')
    success_url = reverse_lazy('list')

# ToDoの削除機能
class TodoDelete(DeleteView):
    template_name = 'todo/delete.html'
    model = TodoModel
    success_url = reverse_lazy('list')

# ToDoの編集機能
class TodoUpdate(UpdateView):
    template_name = 'todo/update.html'
    model = TodoModel
    fields = ('title','contents','priority','duedate')
    success_url = reverse_lazy('list')
~~~
### ToDoアプリケーションのURLを設定

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

**下記のようにコードを入力した**
~~~r:mysite/urls.py 
from django.contrib import admin
from django.urls import path,include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('',include('todo.urls')),
]
~~~
**続いても下記のようにコードを入力した**
~~~r:todo/urls.py
#from django.contrib import admin
from django.urls import path,include
from todo.views import TodoList,TodoDetail,TodoCreate,TodoDelete,TodoUpdate

urlpatterns = [
    path('list/', TodoList.as_view(),name='list'),
    path('detail/<int:pk>', TodoDetail.as_view(),name='detail'),
    path('create', TodoCreate.as_view(),name='create'),
    path('delete/<int:pk>',TodoDelete.as_view(),name='delete'),
    path('update/<int:pk>',TodoUpdate.as_view(),name='update')
~~~

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

まず初めにテンプレート用のディレクトリをtodoディレクトリ内に作成します。

**htmlのベースになるテンプレートを下記のように作成**
~~~r:todo/templates/base.html
<!doctype html>
<html lang="en">
 <head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.1.3/dist/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
    <title>Hello, world!</title>
  </head>
  <body>
    {% block header %}
    {% endblock header %}
    {% block content %}
    {% endblock content %}
</body>
</html>
~~~
このベースから拡張していくことで、繰り返し部分を省略できます。

#### ToDoの一覧表示機能のためのテンプレート

**下記のように作成**
~~~r:todo/templates/todo/list.html
{% extends 'base.html' %}

{% block header %}
<div class="jumbotron jumbotron-fluid">
    <div class="container">
      <h1 class="display-4">TodoList</h1>
      <p class="lead">TodoListを作成して、生産的な毎日を過ごしましょう</p>
    </div>
  </div>{% endblock %}

{% block content %}
    <div class="container">
    <a href="{% url 'create'  %}" class="btn btn-secondary mb-2 " tabindex="-1" role="button" aria-disabled="true">新規作成画面へ</a>
    {% for item in object_list %}
    <div class="alert alert-{{ item.priority }}" role="alert">
        <p>{{ item.title }}  期日{{ item.duedate | date:"o/n/j "}}</p>
        <a href="{% url 'update' item.pk %}" class="btn btn-info " tabindex="-1" role="button" aria-disabled="true">編集画面へ</a>
        <a href="{% url 'delete' item.pk %}" class="btn btn-success " tabindex="-1" role="button" aria-disabled="true">削除画面へ</a>
        <a href="{% url 'detail' item.pk %}" class="btn btn-primary " tabindex="-1" role="button" aria-disabled="true">詳細画面へ</a>
    </div> 
 {% endfor %}
</div>
 {% endblock content %}
~~~

#### ToDoの新規作成機能のためのテンプレート

**下記のように作成**
~~~r:todo/templates/todo/create.html 
{% extends 'base.html' %}

{% block content %}
<form action = '' method = 'POST'>{% csrf_token %}
{{ form.as_p }}
<input type = 'submit' value = 'create'>
</form>
{% endblock content %}
~~~

#### ToDoの詳細表示機能のためのテンプレート

**下記のように作成**
~~~r:todo/templates/todo/detail.html 
{% extends 'base.html' %}

{% block header %}
<div class="alert alert-dark" role="alert">
    detail
</div>
{% endblock %}

{% block content %}
<div class="alert alert-success" role="alert">
    {{ object.title }}
</div>
<div class="alert alert-success" role="alert">
    {{ object.contents }}
</div>
{% endblock content %}
~~~

#### ToDoの削除機能のためのテンプレート

**下記のように作成**
~~~r:todo/templates/todo/delete.html 
{% extends 'base.html' %}
{% block content %}
<form action = '' method = 'POST'>{% csrf_token %}
<input type = 'submit' value = 'delete'>
</form>
{% endblock content %}
~~~

#### ToDoの更新機能のためのテンプレート

**下記のように作成**
~~~r:todo/templates/todo/update.html 
{% extends 'base.html' %}

{% block content %}
<form action = '' method = 'POST'>{% csrf_token %}
{{ form.title }}
{{ form.contents }}
{{ form.priority }}
{{ form.duedate }}
<input type = 'submit' value = 'update'>
</form>
{% endblock content %}
~~~

### ToDoアプリケーションのマイグレーション

DBにToDoアプリケーション用のテーブルを作成する必要があります。
**外側のmysiteディレクトリに移動し、下記のコマンドを実行してください**
~~~r
python manage.py makemigrations
~~~
~~~r
python manage.py migrate
~~~
:::note warn
注意
 * 二回目以降のmakemigrationsは注意
   - すでにレコードが存在する状態で、NULL禁止かつデフォルト値設定なしのフィールドを追加する場合、既存のレコードにNULLが入るのでエラーが発生します
:::

# ToDoアプリケーションのアドミン

**下記のように作成**
~~~r:todo/admin.py
from django.contrib import admin

from .models import TodoModel
# Register your models here.
admin.site.register(TodoModel)
~~~

### アプリケーションの動作確認

これにてプログラムが完成したので、動作確認を行いましょう！
**下記のコマンドでサーバを起動**
~~~r
python manage.py runserver
~~~
サーバを起動したら、[ToDoList](http://127.0.0.1:8080/todo/list/)にアクセス
成功したら下記のようなアプリケーションが表示されます。
![スクリーンショット 2023-08-23 15.59.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3537128/58b25386-e934-d17c-e1a2-bf42715adfac.png)

以上で、ToDoListのアプリケーションが完成です！

このようなシンプルなサイトでさえ、こんなに多くの工程を踏んでいるんですね。
複雑なサイトになると、、、
考えるのはやめておきましょう。

### 余談

これからDjangoを勉強する新人エンジニアに一つだけ言っておきたいことがあります。
丸写しするのは悪い事ではないですが、脳死するのは✖
何を隠そう私が、脳死で丸写しして困った張本人だからです。
各ファイルの役割・処理の流れを理解していないと、エラーが発生した際にどこを修正するべきか全くわかりませんでした。
しかし、各ファイルの役割・処理の流れを理解することで作業効率が大幅に向上しました。
この話を参考に、これから勉強する人は頑張ってみてください!

### 参考サイト
[pythonインストール](https://prog-8.com/docs/python-env
)
[Django 入門](https://docs.djangoproject.com/ja/4.2/contents/)
[ToDoListアプリケーション](https://qiita.com/tsubasa2345/items/140e6b95638ad58a1999)