【初めてのDjango】インストール手順：MVTモデル、ルーティング、DBマイグレーション、views.py、テンプレート、名前空間について

# 概要
* 先日、初めてDjangoをインストールしたので、ここでは最低限の準備ができる状態までの手順を記しておく。
* 基本的にdjangoチュートリアルを見て実施していますが、理解を深めるために自分で調べたことも追記して解説します。
    * [はじめての Django アプリ作成、その 1](https://docs.djangoproject.com/ja/4.1/intro/tutorial01/)

# 環境
* MacBookPro
    * macOS：Monterey
    * チップ：　Apple M1 Pro
    * メモリ：　16GB
* Django：Version 4.1.3


# Djangoをインストールする
* Djangoインストールのコマンドを実施

```
python -m pip install Django
```

* ちなみに、どのバージョンがインストールされているかを調べるコマンドは以下
    * 今回は 4.1.3 と出てくる

```
python -m django --version
```

* コマンドラインから、コードを置きたい場所に cd して、以下のコマンドを実行。
    * `mysite`のところはその都度プロジェクトごとにアレンジする。これにより、mysiteディレクトリの中に、ファイルが自動で生成されます。

```
django-admin startproject mysite
```

# Django のプロジェクトがうまく動作するか確認する
* 外側の mysite ディレクトリに移動ができたら下記のコマンドを実行して、ローカルサーバーを起動する

```
python manage.py runserver
```

下記が出力されれば問題ない。
（適用されていないデータベースマイグレーションについての警告はここでは無視。デフォルトで設定されているマイグレーション情報がデータベースに反映されていない為に表示されるメッセージなので）


```
Watching for file changes with StatReloader
Performing system checks...

System check identified no issues (0 silenced).

You have 18 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.
Run 'python manage.py migrate' to apply them.
December 06, 2022 - 09:37:36
Django version 4.1.3, using settings 'mysite.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
```

* 引数に、IPアドレスとポート番号を指定することができる。デフォルトでは runserver コマンドは内部 IP のポート 8000 で起動。
    * ブラウザでdjangoのページが出てくればOK

```
python manage.py runserver 8080
```

* Ruby on railsとの違い：
    * railsではターミナルで実行可能なrailsコマンドというものがあります。例えばRailsアプリケーションをlocalで立ち上げる時は、`rails s`といった形です。ただ、djangoの場合は、pythonファイルを選んであげるというコマンドなので、rails慣れているとちょっと違和感があるかもです

# アプリケーションを作成する
* アプリケーションを作るには、 manage.py と同じディレクトリに入って、以下のコマンドを実行する。
    * polls というディレクトリを作成するコマンド

```
python manage.py startapp polls
```

* 注意点として、djangoではプロジェクトディレクトリの中に複数のモジュール（アプリケーションのディレクトリ）を作成できる点がある。誤って先述のプロジェクト作成のコマンドを実行しないようにする。

# MVTモデルとは？
* Djangoの場合、MVT(Model View Template)と呼称されるフレームワークを利用します。RailsやLaravelで採用されているMVCモデルと意味はほぼ同じですが、ややこしいのはControllerの役割をView（とURLディスパッチャ）で担い、Viewの役割をTemplateで担うところ。Viewが違うものを指していることが要注意です。
* MVCモデルの魅力は、他の記事でたくさん紹介されていますが、チームで分担して作業できたり、一つのプログラム変更が他に影響を及ぼすことが少なかったりといった点がよく挙げられる点です。

* こちらの記事では、初心者にもわかりやすく、MVTモデルのことが紹介されています
    * [【Django】「MTV（Model – Template – View)」って何？「MVC」と何が違う？](https://biz-navi.site/django-mtv-mvc/)

# ルーティングを設定する（URLconfで紐付けを行う）
* ルーティングについては、こちらの記事が分かりやすかったので参考にしています。プロジェクトディレクトリのurls.pyとアプリケーションディレクトリのurls.py、どちらも設定してあげる必要があります。
    * 参考：[【Django】urls.py：ルーティングの書き方](https://office54.net/python/django/urls-path-include)


## プロジェクトディレクトリのurls.pyとは
* ブラウザから送られるHTTPリクエストに対して、どのページを表示するかという指示をするために、リスト型で「URL」と実行する「関数またはクラス」を指定する
    * djangoチュートリアルでは、「ルートのurls.py」と呼ばれています
    * ルーティングという意味では、Railsでいう`routes.rb`ですね
    * デフォルトでは管理画面のルーティングである`path('admin/', admin.site.urls)`のみが記述されている
    * アプリケーションのurls.pyと連結する場合はこれに記述
    * path関数の引数にはroute（URL パターンを含む文字列）とview（views.pyの関数やクラス）を指定。この二つは必須。ただし、path関数はdjango4以前はurl関数を使っていたため、古いプログラムのソースコードを引っ張ってくる（使用する）時は要注意。
    * プロジェクト内のurls.pyでアプリケーション内のurls.pyを指定する場合は、第二引数にinclude関数を使うことが多い。例えば、`path('', include('polls.urls'))`のように指定することで、pollsディレクトリにあるurls.pyを指定できる。
    * path関数の第三引数のname（URL名称）や第四引数（kwargs）は任意。
    * ちなみに、`'<int:question_id>/'`とURLに入れれば任意の整数であってもヒットする

* path関数（旧url関数） 
```python
path(URL, 関数またはクラス, name=URL名称)
```

* mysite/urls.pyの書き方の例（プロジェクトのurls.py）

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

urlpatterns = [
    path('admin/', admin.site.urls),
    path('polls/', include('polls.urls')),# polls.urlsで指定されたviews.pyの関数template_nameにあるindex.htmlを読み込む。view.pyやtemplateについては後述
    path('', include('polls.urls')), # これを加えるとルートパスでもindex.htmlが読み込まれる
]
```

## アプリケーションディレクトリのurls.pyとは
* ビューを呼ぶために、 urlpatternsという変数に、リスト型で「URL」と実行する「関数またはクラス」を指定する
    * 自動で作成されるものではないので、自分で手動で作成する必要がある
    * djangoは上から順番にマッチするパスを探す
 


* polls/urls.pyの書き方の例（アプリのurls.py）
```polls/urls.py
from django.urls import path

from . import views

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




# models.pyを編集し、DBマイグレーションを実行する

* マイグレーションとは、Django でモデルに対して行った変更 (フィールドの追加やモデルの編集など) をデータベーススキーマに反映させること。データベーススキーマに対するバージョン管理システムのようなものです。
* 手順は以下の通り。
    * `models.py`上のクラス定義を変更する
        * 各フィールドにどのようなデータ型を記憶させるかを記載
    * 以下のコマンドにより、マイグレーションファイルの作成
        * `python manage.py makemigrations`
   * model.pyの内容をデータベースに反映させるための中間ファイルが作成される。初回のデータと、それにつらなる一連の差分データで構成される。
   * 以下のコマンドにより、マイグレーションの実行
       * `python manage.py migrate`


* models.pyの書き方の例
```polls/models.py
from django.db import models


class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published')


class Choice(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    choice_text = models.CharField(max_length=200)
    votes = models.IntegerField(default=0)
```

# views.pyを編集する・テンプレートを作成する
* templates ディレクトリを自分で作成する
* Django に正しいテンプレートを認識させる簡単な方法の一つは、名前空間を与えること
    * templates ディレクトリ下にアプリケーションと同じ名前を付けたディレクトリを用意してそこにテンプレートを置けば [app-name]/index.html という名前でアプリケーションごとのテンプレートを認識できるようになる
* render() 関数とは
    * テンプレートをロードしてコンテキストに値を入れ、テンプレートをレンダリングした結果を HttpResponse オブジェクトで返す、というイディオムは非常によく使われるため、ショートカットがある。
    * 第1引数として request オブジェクトを、第2引数としてテンプレート名を、第3引数（任意）として辞書を受け取る
    * 例えば、以下のように変換できる

```polls/views.py
from django.http import HttpResponse
from django.template import loader

from .models import Question


def index(request):
    latest_question_list = Question.objects.order_by('-pub_date')[:5]
    template = loader.get_template('polls/index.html')
    context = {
        'latest_question_list': latest_question_list,
    }
    return HttpResponse(template.render(context, request))
```

↓↓↓

```polls/views.py
from django.shortcuts import render

from .models import Question


def index(request):
    latest_question_list = Question.objects.order_by('-pub_date')[:5]
    context = {'latest_question_list': latest_question_list}
    return render(request, 'polls/index.html', context)
```

# 名前空間（namespacing）でURLパス指定も可能
* polls.urls モジュール の path() 関数で name 引数を定義すれば、テンプレートタグの {％url％} を使用して、URL 設定で定義されている特定の URL パスへの依存をなくすことができる

* 以下は例：
```polls/templates/polls/index.html
<li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>
```
↓↓↓
```polls/templates/polls/index.html
<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>
```
↓↓↓
* URL 名の名前空間（namespacing）：アプリケーションフォルダのurls.pyでapp nameを定めるとよい。そうすれば、polls以外のアプリケーションが作られてもdjangoが識別できるため。
```polls/templates/polls/index.html
<li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>
```

```polls/urls.py
from django.urls import path

from . import views

app_name = 'polls'
urlpatterns = [
    path('', views.IndexView.as_view(), name='index'),
    path('<int:pk>/', views.DetailView.as_view(), name='detail'),
    path('<int:pk>/results/', views.ResultsView.as_view(), name='results'),
    path('<int:question_id>/vote/', views.vote, name='vote'),
]
```

* 名前空間という考え方は、静的 (static) ファイルでも同じで、templatesの時と同様namespacingしておけば、djangoが検索してくれる。

* テンプレートについては、テンプレートシステムや汎用ビューなどがあるので、チュートリアル3以降をご参照ください。今回は、最低限の準備のところまでとします。引き続き勉強していきます。