【解説付き】Django チュートリアル その2 -前編-

# はじめに

これは、[【解説付き】Django チュートリアル その１](https://qiita.com/murabo/items/b99fd8b137e348b1358c)に続き、
[はじめての Django アプリ作成、その2](https://docs.djangoproject.com/ja/2.2/intro/tutorial02/)に解説をつけて初学者にわかりやすく学んでいただきたいというためのものです。

その2は、ボリュームがあるため前編/後編とさせていただいております。

# 対象
 - pythonはなんとなくわかってるけど、djangoを覚えたい
 - 業務でdjango触ってたけど、きちんと理解したい

上記の方々を対象としています。
環境は、Macを使用して進めていきますので、Windowsユーザーの方は、Windows環境に読み替えてついてきていただければと思います。

# 目次
 - [【解説付き】Django チュートリアル その１](https://qiita.com/murabo/items/b99fd8b137e348b1358c)
 - 【解説付き】Django チュートリアル その2 -前編-
 - [【解説付き】Django チュートリアル その2 -後編-](https://qiita.com/murabo/items/66e56803bf53d39fe640)

# settings.py
`mysite/settings.py` を見ていきましょう
これは、 Django の設定を管理するための Pythonファイルです。

### INSTALLED_APPSの設定
`INSTALLED_APPS` は、Djangoインスタンスの中で有効化したいDjangoアプリケーションの名前を記載します。
`pip install`などで、個別に追加するアプリケーションもここに追加していくことになります。

```python:mysite/settings.py
...
# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]
...
```


デフォルトでは、 よく利用される下記のアプリケーションが、`INSTALLED_APPS` に指定されています。

 - django.contrib.admin - 管理（admin）サイト。後ほど説明します。
 - django.contrib.auth - 認証システム
 - django.contrib.contenttypes - コンテンツタイプフレームワーク
 - django.contrib.sessions - セッションフレームワーク
 - django.contrib.messages - メッセージフレームワーク
 - django.contrib.staticfiles - 静的ファイルの管理フレームワーク

これらのアプリケーションはデータベースを使用するため、事前にデータベース内にテーブルを作る必要があります。
こちらは後ほど説明します。

### Databaseの設定


デフォルトの設定では `SQLite` が指定されています。
データベースに詳しくなかったり、Django を試してみたいだけなら、`SQLite`のままで進めましょう。
`SQLite` は `Python` に標準で組み込まれているため、接続するために何もインストールする必要がありません。
本番環境で使う場合には、`AWS`や`GCP`でも提供されている`PostgreSQL` や `MySQL` などのデータベースが良いでしょう。

公式のチュートリアルでは、`PostgreSQL`を使用しています。

```python:mysite/settings.py
...

# Database
# https://docs.djangoproject.com/en/2.2/ref/settings/#databases

# デフォルトのままの、SQLiteの例
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}

...
```

他のデータベースを使いたい場合、データベースと接続するモジュール(mysql client等)をインストールして、
`settings.py`内にある、`DATABASES`の`default`項目内の以下のキーをデータベースの接続設定に合うように変更してください。

- ENGINE <br/>`'django.db.backends.sqlite3'`、 `'django.db.backends.postgresql'`、 `'django.db.backends.mysql'` または `'django.db.backends.oracle'` のいずれかにします。<br/>※その他のバックエンド も利用可能です。

- NAME <br/>データベースの名前です。<br/>`SQLite` を使用している場合、データベースはコンピュータ上のファイルになります。<br/>その場合、NAME には、そのファイルのファイル名を含んだ絶対パスを指定する必要があります。<br/>デフォルト値は `os.path.join(BASE_DIR, 'db.sqlite3')` で、ファイルはプロジェクトのディレクトリに保存されます。

`SQLite` 以外を使う場合、 データベースに接続するための、`USER` や `PASSWORD` そして `HOST` などの追加設定を加える必要があります。
※詳細については DATABASES のリファレンスドキュメントを参照してください。


###  TIME_ZONE の設定
`mysite/settings.py` で、 タイムゾーンを指定する事ができます。

```python:mysite/settings.py
TIME_ZONE = 'UTC'

USE_TZ = True
```

 - **TIME_ZONE**: TIME_ZONEの指定により、テンプレートで出力される日時情報が変換して出力されます
 - **USE_TZ**: フォームの日付入力欄で、入力した日付が変換されます

※タイムゾーン関連は複雑なため、別途記事にまとめるのでここで詳細は割愛します。

# migrateコマンドを実行する

ここまで設定した内容だけでは、`INSTALLED_APPS`の機能を使うことはできません。
前述した通り、データベースにテーブルが必要です。

マイグレーションファイルから、テーブルを自動的に作成してくれる`migrate`コマンドを実行してみましょう
※マイグレーションファイルについては、この先で触れていきます。

```console
$ python manage.py migrate
```

`migrate` コマンドは `INSTALLED_APPS` に記載されている情報を元に、必要なテーブルを作成します。

※マイグレーションファイルはアプリケーション内に内包されています。


Djangoが作成したテーブルを、確認したい場合は、データベース毎のコマンドで確認できます。

データベースのクライアント上で、

 -  `\dt` (PostgreSQL)
 -  `SHOW TABLES;` (MySQL) 
 -  `.schema` (SQLite)
 -  `SELECT TABLE_NAME FROM USER_TABLES;` (Oracle) 
 
と入力してみましょう。

# Model

## Modelとは

Modelには、データベースの定義と、それに付随するMETAデータをクラスとして記述します。


## Modelの作成

これから開発する `polls` アプリケーションでは、`投票項目 (Question)` と`選択肢 (Choice)` の二つのモデルを作成します。 
`Question` には「質問事項 (question)」と「公開日 (publication date)」 の情報があります。 
`Choice` には「選択肢のテキスト」と「投票数 (vote)」 という二つのフィールドがあります。
`Choice`データは、`Question` に関連づけられる`one to many`となります。

Django では、こうした概念を簡単な Python クラスで表現できます。 
`polls/models.py` ファイルを以下のように編集してください。

```python: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に対してリレーションを貼る
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    choice_text = models.CharField(max_length=200)
    votes = models.IntegerField(default=0)
```


Model(テーブル)は`django.db.models.Model`を継承して、クラスを作ります。
クラスは、テーブルの定義、
クラス変数は、テーブルのカラムの定義となります。

### Modelフィールド
各フィールドは `Field` クラスのインスタンスとして表現されています。
例えば、 `CharField` は文字のフィールド(カラム)で、 `DateTimeField` は日時のフィールド(カラム)です。

#### フィールド名(verbose)
Field の最初の引数には、フィールド名を指定できます。(省略可能)

```python
    question_text = models.CharField("question's text", max_length=200)
```

指定しない場合は、`question_text`のアンダースコアをスペースに置き換えた`question text`となります。


上の例の、`Question.pub_date`の`'date published'`もフィールド名となります。

#### フィールドのパラメータ(引数)
Field クラスの中には必須の引数を持つものがあります。
例えば `CharField` には `max_length` を指定する必要があります。
この引数はデータベースの定義で使われる他、`バリデーション`でも使われます。(後ほど説明)

Field には、オプションの引数もあります。
上記の場合、 `Choice.votes` の default の値を `0` に設定しました。
これは、`Choice`データを作成する際に、`Choice.votes`の値が指定されていなければ、「`0`を設定する」という意味です。


#### ForeignKey(リレーション)

```python:polls/models.py

class Question(models.Model):
    ...


class Choice(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    ...
```

ForeignKeyを使用して、リレーションを定義することができます。
これは、`Choice`が`Question`に関連付けられること意味します。
Django では `多対一(many to one)`、`多対多(many to many)`、そして`一対一(one to one)`のようなデータベースリレーションシップすべてをサポートします。

今回の構造だと下記のようなテーブルとなります。

**Questionテーブル**

|id|question_text|pub_date|
|:--|:---|:---|
|1|好きな食べものは何ですか？|2019-08-16 00:00:00|

**Choiceテーブル**

|id|question_id| choice_text | votes|
|:--|:---|:---|:--|
|1|1|和食|0|
|2|1|洋食|0|
|3|1|イタリアン|0|

## プロジェクトにアプリケーション(パッケージ)を追加する
この段階では、プロジェクトが`polls`アプリケーションを認識できていません。

`polls`をプロジェクトに追加させることで、
前述したModelファイルを使って、アプリケーションのデータベーススキーマ(migrateファイル)を作成 (CREATE TABLE 文を実行) できます。




アプリケーションを認識させるには、`settings.py`の`INSTALLED_APPS`に追加する必要があります。

`PollsConfig`クラスは、 `polls/apps.py`にあるので、指定する記述は`'polls.apps.PollsConfig'`となります。 

下記のように`mysite/settings.py` を編集し、 `INSTALLED_APPS`に上記のパスを追加します。

```python:mysite/settings.py
...
INSTALLED_APPS = [
    # pollsパッケージの追加
    'polls.apps.PollsConfig',
    'django.contrib.admin',
    ...
]
...
```

これで Django は、`polls`アプリケーションを認識することができます。


## migrate

※チュートリアル通した後に見返すくらいでも良いかもしれません。軽く読み進めたい人は[migrateのポイント](#migrateのポイント)に進めてもらって大丈夫です。

`makemigrations`コマンドを使って、マイグレーションファイルを作成します。

**マイグレーションファイル** ・・・データベースを作成・変更するためのスキーマの定義情報

```console
$ python manage.py makemigrations polls
```

コマンドが成功すると、下記のようなメッセージが出力されます
(選択しているデータベースにより若干メッセージは変わります)

```console
Migrations for 'polls':
  polls/migrations/0001_initial.py:
    - Create model Choice
    - Create model Question
    - Add field question to choice
```
 




`makemigrations` を実行すると、Modelの変更内容(この場合、新しいものを作成しました)を、マイグレーションファイルとして生成されます。

マイグレーションはモデル(と、データベーススキーマ情報)の変更をファイルとして保存する方法です。
生成されたマイグレーションをファイル `polls/migrations/0001_initial.py` は、ファイルを開いて確認することもできます。
マイグレーションのファイルを作成するたびに、毎回読む必要はありません。
ですが、マイグレーションの変更内容を微調整したいという時は、修正も可能です。


Django には、マイグレーションを自動的に実行し、データベーススキーマを更新・管理するためのコマンドがあります。これは `migrate` と呼ばれるコマンドで、この後すぐに見ていきます。
その前に、マイグレーションがどんなSQLを実行するのか見てみましょう。 
`sqlmigrate` コマンドはマイグレーションの名前を指定し、実行されるSQLを出力します。




```console
$ python manage.py sqlmigrate polls 0001
```


次のような結果が表示されます。

```sql:チュートリアルのPostgreSQLの出力
BEGIN;
--
-- Create model Choice
--
CREATE TABLE "polls_choice" (
    "id" serial NOT NULL PRIMARY KEY,
    "choice_text" varchar(200) NOT NULL,
    "votes" integer NOT NULL
);
--
-- Create model Question
--
CREATE TABLE "polls_question" (
    "id" serial NOT NULL PRIMARY KEY,
    "question_text" varchar(200) NOT NULL,
    "pub_date" timestamp with time zone NOT NULL
);
--
-- Add field question to choice
--
ALTER TABLE "polls_choice" ADD COLUMN "question_id" integer NOT NULL;
ALTER TABLE "polls_choice" ALTER COLUMN "question_id" DROP DEFAULT;
CREATE INDEX "polls_choice_7aa0f6ee" ON "polls_choice" ("question_id");
ALTER TABLE "polls_choice"
  ADD CONSTRAINT "polls_choice_question_id_246c99a640fbbd72_fk_polls_question_id"
    FOREIGN KEY ("question_id")
    REFERENCES "polls_question" ("id")
    DEFERRABLE INITIALLY DEFERRED;

COMMIT;
```

```sql:SQLiteの出力

BEGIN;
--
-- Create model Question
--
CREATE TABLE "polls_question" (
    "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, 
    "question_text" varchar(200) NOT NULL, 
    "pub_date" datetime NOT NULL
);
--
-- Create model Choice
--
CREATE TABLE "polls_choice" (
    "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, 
    "choice_text" varchar(200) NOT NULL, 
    "votes" integer NOT NULL, 
    "question_id" integer NOT NULL REFERENCES "polls_question" ("id") DEFERRABLE INITIALLY DEFERRED
);
CREATE INDEX "polls_choice_question_id_c5b4b260" ON "polls_choice" ("question_id");
COMMIT;
```


以下のポイントを覚えておいてください

- `sqlmigrate`の出力は、使用しているデータベースによって異なります。
- テーブル名はアプリケーションの名前 (`polls`) とモデルの小文字表記 の `question` と `choice` を組み合わせて、`polls_question`や`polls_choice`のように自動的に生成されます。 (指定することも可能です)
- 主キー (primary key, ID) は自動的に追加されます (この挙動もオーバライド可能です)。
- Django は外部キーのフィールド名に "_id"(今回は`question_id`) を追加します。(指定することも可能です)
- 外部キーリレーションシップは FOREIGN KEY 制約で明確化されます。 DEFERRABLE の部分については心配しないでください。 PostgreSQLに外部キーをトランザクション終了まで強制しないようにするためのものです。
- 使用するデータベースに合わせて、`auto_increment (MySQL)`、 `serial (PostgreSQL)` もしくは `integer primary key autoincrement (SQLite)` のようなデータベース固有の型が自動的に選択され生成されます。フィールド名のクォーティング (例えば、ダブルクォートにするか、シングルクォートにするか) も同様です。
- `sqlmigrate` コマンドはデータベースに**`マイグレーションは実行されません`**。実行されるSQLが何であるかをスクリーンに表示するだけです。これはDjangoが何をしようとしているかを確認したり、データベース管理者に変更のためのSQLスクリプトを要求されているときに役に立ちます。


もし興味があれば `python manage.py check` を実行することもできます。 これはマイグレーションを作成したりデータベースに影響なく、プロジェクトに何か問題がないか確認します 。


`migrate` を再度実行し、 Modelで定義したテーブルを作成しましょう。


```console
$ python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, polls, sessions
Running migrations:
  Rendering model states... DONE
  Applying polls.0001_initial... OK
```


`migrate` コマンドは、未適応のマイグレーションファイルを探索し、データベースに対して実行していきます。
重要なのは、モデルに対して行った変更はデータベースのスキーマに同期するということです。

(Djangoはデータベース内の django_migrations テーブルを利用して、マイグレーションの変更履歴を管理しています。)


### migrateのポイント
今の段階では、Modelの変更を実施するための3ステップガイドを覚えておいてください
(最低限、ここだけ押さえておけば、問題ないと思います)

 - Modelを変更する (models.py のクラスを修正)
 - 変更状態のマイグレーションファイルを作成するために `python manage.py makemigrations` を実行します。
 - データベースに変更を適用するために `python manage.py migrate` を実行します。

マイグレーションの作成と適用のコマンドが分割されている理由は、マイグレーションをバージョン管理システムにコミットし、アプリとともに配布するためです。これによって、あなたの開発が容易になるだけでなく、他の開発者や本番環境にとって使いやすいものになります。





# まとめ

ここまでがdjango チュートリアルのその2 -前編- でした。

既にdjangoに触れてる人や、多言語に詳しい人ならわかるけど、初めての人にはわかりにくい部分が多いと思います。
データベースやテーブルなど、初学者には壁となるポイントが出てくるかと思います。
まずは、ここでの理解は無くても良いので、先に進めてみてください。

チュートリアルやドキュメントは何度も読み返して知識を積み重ねていければ良いと思います。

次は、[【解説付き】Django チュートリアル その2 -後編-](https://qiita.com/murabo/items/66e56803bf53d39fe640)に読み進めてもらえると幸いです。


<br/>
[PR]
[![長方形-システム開発002.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38197/36301a76-6238-c8f4-1cb8-691642889ca0.png)](https://www.co.task.site?pr=qiita)