10 分で終わる Django の実用チュートリアル

Django の公式チュートリアル polls は冗長なので苦痛でした。
備忘録を兼ねて気楽なチュートリアルを作成しました。
実用的なことを短時間でひと通り見てみたい人向きです。
メモを書いたり編集したりする簡単なアプリが作れます。

コード部分をコピペして動かしてから中身を調べて下さい。
これが済んだら polls をもう少し楽に読めるかも。

2020-03-28 更新: windows10/Python-3.8.2-amd64/Django-3.0.4
2018-06-27: windows10/Python-3.6.5-amd64/Django-2.0.6

# 前作業
## プロジェクトの作成

ソースを置きたい場所で以下を実行しプロジェクトを新規作成。
プロジェクト名は mysite だとする。※1

```
django-admin startproject mysite
```

作成したディレクトリ mysite に入ってアプリケーションを新規作成。
アプリケーション名は memo だとする。※2

```
cd mysite
python manage.py startapp memo
```

※1 プロジェクトとは大きく仕事をくくった単位（例えば「健康管理」）のこと。
※2 アプリケーションとはひとつひとつの仕事（例えば「体重記録」とか「ジョギング記録」）のこと。

ここまでのファイルの配置はこうなる

```
mysite/
    mysite/
        __pycashe__/    <- 気にしなくていい
        setting.py, urls.py など *.py が 5 個
    memo/
        migrations/     <- 気にしなくていい
        models.py, views.py など *.py が 6 個
    manage.py           <- プロジェクトの管理用
```

mysite/ の中に mysite/mysite/ というのがあって、ややこしい。
mysite/ は各アプリを含むプロジェクト全体を格納するフォルダ。
mysite/mysite/ は全アプリに共通する部分を保存するフォルダ。


## プロジェクトを構成するアプリの登録

mysite/mysite/settings.py の INSTALLED_APPS を編集。
プロジェクト mysite の中にアプリケーション memo がある、と指定する。
settings.py は少しデカいのでの INSTALLED_APP のところの部分のみ記載。
あとは変更しない。



```py:mysite/mysite/settings.py
INSTALLED_APPS = [
    'memo.apps.MemoConfig',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]
```


## URL の設定

ブラウザで Web アプリケーションにアクセスするときの url を指定する。

mysite/mysite/urls.py を編集（コメントを除きファイル全体を掲載）

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

urlpatterns = [
    path('memo/', include('memo.urls')),
    path('admin/', admin.site.urls),
]
```


mysite/memo/urls.py を新規作成（ファイル全体を掲載）
新たにファイルを作るとき、設置場所は注意が必要。


```py:mysite/memo/urls.py
from django.urls import path
from . import views

urlpatterns = [
    path('', views.MemoListView.as_view()),
    path('index', views.MemoListView.as_view(),
        name = 'site_index'),
    path('create', views.MemoCreateView.as_view(),
        name = 'site_create'),
    path('detail/<int:pk>', views.MemoDetailView.as_view(),
        name = 'site_detail'),
    path('update/<int:pk>', views.MemoUpdateView.as_view(),
        name = 'site_update'),
    path('delete/<int:pk>', views.MemoDeleteView.as_view(),
        name = 'site_delete'),
]
```

url を探すときは、まず最初に mysite/mysite/urls.py を参照する。
次に include された mysite/memo/urls.py も参照する、という順番。 


### 参考（最初は分からなくても動きます）

- path の引数は path(UrlString, View, option)
    - option のところで name に値を指定すると url の代わりにこの名前が使えるようになる。
    - どれがどれだか混乱しないように、ちょっとくどい名前を付けた。
- as_view() はクラスビューの表示関数。
- pk は、django がそれぞれのメモに自動で付けたプライマリキー番号。



# アプリケーション本体の作成

モデル、ビュー、テンプレートの 3 つを作ると、アプリケーションが作れる。

## モデルの作成

「モデル」というのは、Web アプリが保持するデータ（例えば「日付」とか「体重」など）のこと。
単純なアプリなのでデータのフィールドは１つ（テキストで書いたメモ）だけ。

mysite/memo/models.py を修正（ファイル全体を掲載）

```py:mysite/memo/models.py
from django.db import models

class Memo(models.Model):
    textfield = models.CharField(max_length = 200)
```

もっと複雑なデータの場合は、ここにデータのモデルを追加していくことになる。

## ビューの作成

「ビュー」とはブラウザで表示するときの各ページのこと。ここでは、

1. 全メモの一覧
2. 新規メモ作成
3. メモの詳細表示
4. メモの編集
5. メモの消去

の 5 つのページを作る。

mysite/memo/views.py を修正（ファイル全体を掲載）

```py:mysite/memo/views.py
from django.views import generic
from .models import Memo

class MemoListView(generic.ListView):
    model = Memo
    context_object_name = 'memo_context_list'

class MemoCreateView(generic.CreateView):
    model = Memo
    fields = ['textfield']
    success_url = '/memo'
    template_name = 'memo/memo_create.html'

class MemoDetailView(generic.DetailView):
    model = Memo
    template_name = 'memo/memo_detail.html'
    context_object_name = 'memo_context'

class MemoUpdateView(generic.UpdateView):
    model = Memo
    fields = ['textfield']
    success_url = '/memo'
    template_name = 'memo/memo_update.html'

class MemoDeleteView(generic.DeleteView):
    model = Memo
    success_url = '/memo'
    template_name = 'memo/memo_delete.html'
```

### 注意点

- generic.XxxView というのは「汎用ビュー」というもので、使いまわしができるように設計されたビュークラス。
- ブラウザに表示するときの体裁は、このあと説明する「テンプレート」で指定。
- `context_object_name` はコンテクスト（後述）の名前
  - 名前を指定しなければ `object_list` という名前がデフォルトで付けられる。
  - django の汎用ビューシステムは、変数やファイルのデフォルト名が悪いのが欠点
  - だから、あえて自分で名前を明示的に付けた。
- `template_name` はテンプレートファイルのファイル名
  - 明示的に指定しなければ、ビューごとに固有のデフォルトの名前が付けられる。
  - デフォルト名が規則的でないし分かりにくいので、自分で分かりやすい名前を付けた。
  - ファイル名を明示的に指定するときは、`memo/memo_delete.html` のようにディレクトリ名が必要

## テンプレートの作成

フォルダ mysite/memo/templates/memo を新規作成し 5 つの html ファイルを新規作成。
テンプレートの html ファイルは、通常の html に django 用の文法が追加されている。
5 つの html ファイルは、5 つのビューにそれぞれ対応している。
フォルダ名は template ではなく templates で、最後に s が付いていることに注意。


・memo_list.html（ファイル全体を掲載）

ビュークラスはテンプレートにコンテクスト（操作用変数の塊みたいなもの）を送って表示させる。
`memo_context_list` というのがコンテクストで `views.py` で自分で名前を付けている。
`temp` は、コンテクストからひとつずつ取り出した Memo オブジェクト。

```html:mysite/memo/templates/memo/memo_list.html
<p>View : MemoListView</p>
<p>Template : memo_list.html</p>

{% if memo_context_list %}
  <table>
  {% for temp in memo_context_list %}
    <tr>
      <td><a href = "{% url 'site_detail' temp.pk %}"> {{ temp.textfield }}</a></td>
      <td><a href = "{% url 'site_update' temp.pk %}">Edit</a></td>
      <td><a href = "{% url 'site_delete' temp.pk %}">Delete</a></td>
    </tr>
  {% endfor %}
  </table>

{% else %}
<p>No memo available.</p>

{% endif %}
<p><a href = "{% url 'site_create' %}">Create a new memo.</a></p>
```

・memo_create.html（ファイル全体を掲載）

フォームを扱う汎用ビューは、コンテクストではなく form オブジェクトをテンプレートに送る。
` {{ form.textfield }} ` のようにして中身のフィールドを取り出せるが、` {{ form.as_p }} `と書く方がよく使われている。
as_p はフォームの中身を\<p\>でくくって列挙するというもので便利だが、ブラックボックスで中身が分かりにくいので、あえて使わなかった。
as_table といった書き方もある。

```html:mysite/memo/templates/memo/memo_craete.html
<p>View : MemoCreateView</p>
<p>Template : memo_create.html</p>

<form method = "post">
    {% csrf_token %}
    {{ form.textfield }}
    <input type = "submit" value = "Create" />
</form>

```


・memo_detail.html（ファイル全体を掲載）

ビューから送られてくるコンテクストの名前は memo_context。
pk というのは、このメモのプライマリキー番号で、django が自動で割り振っている。

```html:mysite/memo/templates/memo/memo_detail.html
<p>View : MemoDetailView</p>
<p>Template : memo_detail.html</p>

{% if memo_context %}
  <ul>
    <li>Primary Key Number : {{ memo_context.pk }}</li>
    <li>Memo Text : {{ memo_context.textfield }}</li>
  </ul>

{% else %}
<p>No memo available.</p>

{% endif %}
<p><a href = "{% url 'site_index' %}">index</a></p>
```


・memo_update.html（ファイル全体を掲載）

```html:mysite/memo/templates/memo/memo_update.html
<p>View : MemoUpdateView</p>
<p>Template : memo_update.html</p>

<form method = "post">
    {% csrf_token %}
    {{ form.textfield }}
    <input type = "submit" value = "Save" />
</form>

```

・memo_delete.html（ファイル全体を掲載）

```html:memo_delete.html
<p>View : MemoDeleteView</p>
<p>Template : memo_delete.html</p>

<form method = "post">
    {% csrf_token %}
    <input type= "submit" value = "Delete" />
</form>
```

ここまでのファイル配置はこうなる

```
mysite
├── mysite
│   ├── __pycashe__    <- 気にしなくていい
│   └── setting.py, urls.py など *.py が 5 個
├── memo
│   ├── migrations     <- 気にしなくていい
│   ├── models.py, views.py などに urls.py を追加し *.py が 7 個
│   └── templates
│       └──memo
│          └── memo_list.html など *.html が 5 個
└── manage.py
```


### 参考（最初は分からなくても大丈夫）

- {{ varname }} は変数値を HTML に埋め込んでくれる
- {% if %}、{% endif %}、{% else %}、{{% for %}}、{{% endofor %}} は django テンプレート特有の文法
    - 役割は想像のとおり
- {% crsf_token %} は、form に付け加えることで crsf 攻撃から守ってくれる呪文。



# 仕上げ
## マイグレーションの実施
データベースへの保存の準備を行う。
マイグレーションするとデータベース (SQLite) に変更が反映される。
モデルを変更したりなど、大変更があるたびに実施が必要。


mysite/ で以下を実行。

```
python manage.py makemigrations
python manage.py migrate
```


## 動作の確認

mysite/ で以下を実行すると簡易 http サーバーが起動する。

```
python manage.py runserver
```

これでブラウザで見られるようになる。以下の URL で画面が出てきたら完成。
ポートはデフォルトで 8000 番なので注意。

```
http://localhost:8000/memo/
```