Djangoのお勉強(2) 


[はじめての Django アプリ作成、その2](https://docs.djangoproject.com/ja/2.0/intro/tutorial02/)の内容にそって試したことや調べた事を記載する。

前回の記事は [Djangoのお勉強(１) ](https://qiita.com/ShimantoAkira/items/ec1130fbd2a65cd5b7f3)

# Database の設定
Databeseの設定は<プロジェクト名>/settings.pyのファイル中下記のように定義されている

```python:<プロジェクト名>/settings.py
# Database
# https://docs.djangoproject.com/en/2.0/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}
```
**'ENGINE'**は、使用するDatabaseエンジンの種別を表し、
'django.db.backends.sqlite3'
'django.db.backends.postgresql'
'django.db.backends.mysql'
'django.db.backends.oracle
のいずれかを指定する。色々DBを試してみるのは面倒なので、当分sqliteを使うことにする

**'NAME'**は、Databaseの名前を示す。
sqliteは、ファイルを直接アクセスするので、ファイルを指定する事になる。
他のDBだったら設定方法が変わるけど面倒なので調べない。
[DATABASES](https://docs.djangoproject.com/ja/2.0/ref/settings/#std:setting-DATABASES)に説明が書いてあるらしい

os.path.join(BASE_DIR, 'db.sqlite3')は、そのまんま、DBとなるファイル名を示している。
BASE_DIRは、settings.pyの上の方で、下記のように定義されていて、settings.pyが置かれているディレクトリのひとつ上のディレクトリを指している

```python:
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
```
os.path.join()は、ディレクトリ名とファイル名を連結する関数なので、結局、os.path.join(BASE_DIR, 'db.sqlite3')は、
"<プロジェクトディレクトリ>/db.sqlite3"というファイルを指している。

# ローカライズ設定
タイムゾーンと言語コードは<プロジェクト名>/settings.pyのファイル中下記のように定義されている

```python:<プロジェクト名>/settings.py
# Internationalization
# https://docs.djangoproject.com/en/2.0/topics/i18n/

LANGUAGE_CODE = 'en-us'

TIME_ZONE = 'UTC'
```
日本むけに変更する場合は下記のように変える

```python:<プロジェクト名>/settings.py
# Internationalization
# https://docs.djangoproject.com/en/2.0/topics/i18n/

LANGUAGE_CODE = 'ja'

TIME_ZONE = 'Asia/Tokyo'
```

# とりあえずDatabaseを作る
ここまで設定したら、まずはDatabaseを作ってみる
プロジェクトのルートディレクトリ(BASE_DIRで示されているディレクトリ)terminalから下記のコマンドを実行する

> python manage.py migrate

```text:実行結果
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying auth.0009_alter_user_last_name_max_length... OK
  Applying sessions.0001_initial... OK

```


# modelを作る

django.db.models.Modelを継承したクラスで、データベースのテーブル構造を定義する

[チュートリアル](https://docs.djangoproject.com/ja/2.0/intro/tutorial02/)に掲載されているソースでは、下記のように定義している

```python:poll/model.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)
```
models.CharFieldは文字列のフィールド
models.DateTimeFieldは、日付時刻フィールド
models.ForeignKeyは、リレーションを表すフィールド
models.IntegerFieldは、整数フィールド
を、それぞれ表し、[モデルフィールドリファレンス](https://docs.djangoproject.com/ja/2.0/ref/models/fields/)で定義されている

各クラス名（この場合、QuestionとChoice）は、DB上ではテーブル名に対応し、
各変数（question_text、pub_date、question、choice_text、votes）は、DB上ではフィールド名になる

ForeignKeyは、リレーションシップを表し、下記のインタフェースになっている

**class ForeignKey(to, on_delete, **options)**
toは、リレーション先を示す。この場合はQuestionクラスのIDを保持する事を示す
on_deleteは、レコードが削除された時の振る舞いを指定する

- CASCADE
　カスケード削除。 DjangoはSQL制約ON DELETE CASCADEの動作をエミュレートし、ForeignKeyを含むオブジェクトも削除します。
- PROTECT
　django.db.IntegrityErrorのサブクラスであるProtectedErrorを呼び出すことによって、参照されるオブジェクトの削除を防止します。
- SET_NULL
 ForeignKey nullを設定します。 nullがTrueの場合にのみ可能です。
- SET_DEFAULT
 ForeignKeyをデフォルト値に設定します。 ForeignKeyのデフォルトを設定する必要があります。
- SET（）
 ForeignKeyをSET（）に渡された値に設定するか、または呼び出し可能なものが渡された場合は、その結果を呼び出します。ほとんどの場合、models.pyのインポート時にクエリを実行しないようにするには、呼び出し可能コードを渡す必要があります：

```python:
from django.conf import settings
from django.contrib.auth import get_user_model
from django.db import models

def get_sentinel_user():
    return get_user_model().objects.get_or_create(username='deleted')[0]

class MyModel(models.Model):
    user = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET(get_sentinel_user),
    )
```
- DO_NOTHING
 何もするな。データベースバックエンドで参照整合性が強制されている場合、SQL ON DELETE制約をデータベースフィールドに手動で追加しない限り、IntegrityErrorが発生します。

(by google翻訳）

# modelを有効にする

最初に、このアプリケーションを登録する

登録するアプリケーションの設定を保持しているクラスは、<アプリ名>/apps.pyにある。

```pools/apps.py
from django.apps import AppConfig


class PollsConfig(AppConfig):
    name = 'polls'
```
この場合pollsConfigが、設定を保持するクラスになるが、このクラス名はアプリケーション生成時に、Djangoによって自動的に命名される。
デフォルトの状態で、設定されているのはnameだけであるが、他の設定項目は[Configurable attributes](https://docs.djangoproject.com/ja/2.0/ref/applications/#configurable-attributes)に記載されている。

このクラスを<プロジェクト名>/settings.pyファイルに登録する
今回、このPollsConfigは、polls/apps.pyの中にあるので、'polls.apps.PollsConfig',を登録する。

```<プロジェクト名>/settings.py
# Application definition

INSTALLED_APPS = [
    'polls.apps.PollsConfig',      # これを登録した
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]
```

この設定をして

> python manage.py makemigrations polls

を実行するとマイグレーションが作成される。実行結果は下記の通り

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

> python manage.py sqlmigrate polls 0001

とやると、実際に発行されたsql文が表示される

```sql:
BEGIN;
--
-- Create model Choice
--
CREATE TABLE "polls_choice" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "choice_text" varchar(200) NOT NULL, "votes" integer NOT NULL);
--
-- 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);
--
-- Add field question to choice
--
ALTER TABLE "polls_choice" RENAME TO "polls_choice__old";
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);
INSERT INTO "polls_choice" ("id", "choice_text", "votes", "question_id") SELECT "id", "choice_text", "votes", NULL FROM "polls_choice__old";
DROP TABLE "polls_choice__old";
CREATE INDEX "polls_choice_question_id_c5b4b260" ON "polls_choice" ("question_id");
COMMIT;
```
これを実際にdatabaseに反映するには、ターミナルより

> python manage.py migrate

を実行する。実行結果は以下の通り。

```text:
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, polls, sessions
Running migrations:
  Applying polls.0001_initial... OK
```

[Db Browser for SQlite](http://sqlitebrowser.org/)で内容を確認してみる

![Django01.jpg](https://qiita-image-store.s3.amazonaws.com/0/99652/d21de75d-643a-ff52-e328-85f392fb3421.jpeg)

polls_choiseとpolls_questionと２つのテーブルが出来ていることがわかる。
（その他にも、沢山出来ているけど）

Brouse Dataタブを選択し、テーブルの内容を確認する
![Django02.jpg](https://qiita-image-store.s3.amazonaws.com/0/99652/7876c637-ec52-54f3-daa3-aeb46d5495a6.jpeg)

polls_choiseテーブルにid,choice_text,votes,questionのフィールドが出来ている事がわかる。
idは勝手に作られる。