Django 開発者への道① ~ Modelsを理解する ~

### こんにちは。
### 普段は某ITスタートアップで、Djangoを使って、サービス開発をしています！！

## これからステップごとに、Django でプロダクトを開発していくために必要な技術を１からおさらいしていきます!

**元記事**
[Django: DBを扱うmodelsについて](https://just-python.com/article/article-django-models)

## 第一弾 ~ Modelsを理解する ~ 構成

1. Modelsの立ち位置
2. テーブルの作成
3. フィールドの追加

#### 尚、Djangoのプロジェクトは既に立ち上げてるという前提でお話をしていきます。

**以下のスキルセットは、既に習得している前提です。**

- virtualenvで仮想環境の構築ができる
- pip install でdjangoなどのライブラリのインストールができる
- django-admin startproject で プロジェクトを立ち上げることができる
- python manage.py startapp でアプリケーションをプロジェクトに追加することができる


## 1. Modelsの立ち位置

#### 今回は、モデルを理解していくために、 models.py に記述するDBのテーブルのフィールドの内容などを見ていきます。

アプリケーションをプロジェクトに追加（startapp）すると、views.py と models.py、その他のファイルがプロジェクトに追加されます。

**例**
blog という アプリケーションを追加すると、プロジェクトに blogというディレクトリが追加されます。
blogディレクトリの中には、

- admin.py
- app.py
- models.py
- tests.py
- views.py

が自動的に追加されています。

このblogというアプリケーションにおいて、DBの設計を models.py で行なっていきます。


## 2. テーブルの作成

#### まずは、テーブルを作成する方法について、学習していきます。

下記の要件のテーブルを作るとします。

**article**

- 記事名（title)
- イメージ写真(image)
- 本文（body）
- 投稿者(creator)
- 更新者(modifier)
- 投稿日(created)
- 更新日(modified)



以下のように記述することで、テーブルを作成することができます。

```py
class Article(models.Model):
    title = models.CharField()
    image = models.ImageField()
    body = models.TextField()
    creator = models.CharField()
    modifier = models.CharField()
    created = models.DateTimeField()
    modified = models.DateTimeField()
```

上記にある models は django.db というところから、dbのコードをimportしています。（下記のように、初めから書かれています。）

```
from django.db import models
```


テーブルの作成はこれだけでできます！！
なお、id(primary key/pk)については、何も記述しなければ、Djangoが自動で作成してくれるので、わざわざ毎回、記述必要する必要はありません。
素晴らしいですね :) 

そして、さらにテーブルを色々カスタマイズしていくことも可能です。
例えば、articleテーブルを、別のテーブルから参照したい場合、
本来は、自動で作成されるテーブルのIDが表示されます。
**例**
```
<Instance "table" 1>
```
上記のような形だと非常に見づらいので、記事の名前を返すようにできるとわかりやすくなると思います。

そのような仕様を入れる場合、classの最後に以下のようなコードを追加します。

```py
class Article(***):
    title = models.CharField()
    ****
    ****
   
    def __str__(self):
        return self.title
```

上記のように書くことで、特定のArticleテーブルを参照した時、記事のタイトルが返るようになります。


## 3. フィールドの追加

#### 実際に、テーブルを書くためには、フィールドの知識が必要になって来ます。

よく使うフィールドをここでは覚えておきましょう！

- CharField (文字列が入る)
- TextField(文章が入る)
- ImageField(写真ファイルが入る)
- FileField(不特定のファイルが入る)
- IntegerField(数値が入る)
- BooleanField(True/False)が入る *いわゆるフラッグ
- DateField(日付が入る)
- DateTimeField(日時が入る)
- ForeignKey(外部キー) *一対多のリレーションになる。（言葉だけでも覚えておきましょう！）
- ManyToManyField(複数の外部キーが入る) *多対多のリレーションになる
- OneToOneField(外部キー) *一対一のリレーションになる 


一つずつ見ていきましょう！！

### CharField 

主に、文字が入ります。（改行はできません。）

**注意点**

- 文字数制限をかけないといけない
    - max_length = 255 などで、数字を明示的に指定します。

以上！

### TextField

主に、文章が入ります。（改行が可能。）
- 文字数制限をかけることもできます。（マストではない。）

*豆知識

- 外部ライブラリなどを使うことで、リッチテキストや、マークダウン式の形のフィールドもTextFieldのように作成することが可能です。

### ImageField

主に、写真ファイルが入ります。

*注意点

- upload_toを指定しなければいけない。
    - 例:  upload_to="image"
    - 上記のようにすることで、プロジェクト/media/image/ のなかに、アップロード写真が入ります。
    - 実際に、mediaの中に、写真ファイルが入り、dbには、写真へのパスが保存されます。
- 何もしなければ、jpgファイルしか受け付けません。
    - .pngなどを扱うためにはpythonのイメージを取り扱う pillowというライブラリが必要になります。

### FileField 

主に、不特定のファイルが入ります。
注意点は、ImageFieldを参照。

### IntegerField

主に、下記の範囲で数値が入ります。
2147483648 ~ -2147483647

例えば、扱う数値が低いとわかっている場合は、
SmallIntegerFieldなども存在します。
下記の範囲で数値が入ります。
32768 から -32767

反対に、かなり大きな数値を扱えるBigIntegerFieldも存在します。 

また、フィールドの引数として、数値の下限や上限を決めたりすることもできます。


### BooleanField

主に、True か False が入ります。
Pythonのbool型として扱われます（文字列ではありません）
*よく使います。
使用例

- 公開フラグ
- 削除フラグ
- etc...


### DateField, DateTimeField

主に、時間を扱うフィールドです。
DateFieldの場合は、日にちまでしか、入りません。
DateTimeField の場合は、秒数まで入ります。
また、BooleanFieldと同じく、PythonのDate(Datetime)型として扱われます。

### ForeignKey 

主に、外部キーを扱うフィールドで、他のテーブルと紐づけるために使われます。
外部キーについては、別途、別のシリーズで扱います。

例えば、Categoryというテーブルがあり、

```py
class Category(models.Model):
    name = models.CharField()
```
上記のようなテーブルで、
nameが政治というカテゴリが存在するとしましょう。

Article テーブルが以下のような場合、

```py
class Article(models.Model):
    title = models.CharField()
    category = models.ForeignKey(Category, on_delete=models.CASCADE)
```

例えば、"トランプ政権崩壊"というtitleの記事を作成した場合に、先ほどの"政治"というカテゴリのテーブルと紐づけることができます。

*注意点

- ForeignKeyを作る場合、on_delete という引数を指定しないとエラーが出ます。これは、参照先が、削除された場合に、紐づいているテーブルをどうするか、というものです。
    - on_delete = models.CASCADE 
        - 上記のようにした場合は、該当のArticleテーブルは一緒に削除されます。
    - on_delete = models.PROTECT
        - 上記の場合、該当のテーブルは、削除されなくなります。
    - その他にもたくさんありますが、ここでは触れません・


尚、ForeignKeyにおいて、 Articleテーブル から見た Categoryテーブルは、一つのみですが、
Categoryテーブルから見たArticleテーブルは、複数存在することがあります。
なので、リレーションは 一対多 になります。


### ManyToManyField

主に、多対多のリレーションを作るときに、使います。
* on_delete は必要ありません。

### OneToOneField

主に、一対一のリレーションを作るときに、使います。
* 仮に複数紐づけようとするとエラーになるので、気をつけてください。

ManyToManyとOneToOne　は少し、難易度が高いので、ここでは、最小限しか触れないでおきます。

---

そのほかにも、指定できるフィールドには下記のものがあります。（名前と内容だけ）

### その他のField

- URLField (URLパスを入れることができる)
- SlugField (URLのパスに対応した文字しか入れれない文字列 -> 半角英数字など)
    - 正規表現などで制限をかける必要がなくなる
- EmailField (メールアドレスを入れることができる。@がない場合などに弾くことができる)
- PositiveIntegerField(マイナスの数字が絶対ない場合、Positiveを入れることで、容量を最適化できる)

ほかにもたくさんあります。



ほとんど全てのフィールドには、共通して指定できるパラメーターがあります。

- null
フィールドがnullになっても良いかどうか
例: null=True
ケース: 既存のテーブルに後から、フィールドを追加する場合、null=Trueにすることで、既に存在しているデータに追加された新フィールドの値をnullにすることができる

- blank
フィールドが空白になっても良いかどうか
例: blank = True
ケース: 値が空白でもOKかどうかを設定できる。上記のArticle において、カテゴリがなくても、データを作成したい場合、 blank=Trueにすると、実現できる

- default 
デフォルト値を設定できる
例: default = 1
ケース: 初めから、値を設定することができる

- verbose_name
フィールドの表示名を変更できる
例: verbose_name = "記事タイトル"
ケース: Articleテーブルのデータを取得した時に、表示名を設定できる。本来、英語で、 title と返るが、日本語で、タイトル などに設定することができる。


ほかにも共通して設定できることがありますが、主に使うのは、上記の４点です！


他にも、class 自体に、様々な関数を設定することができますが、ここでは、最低限に留めておきます。
その他の、modelsの設計については、[公式ドキュメント](https://docs.djangoproject.com/ja/2.2/intro/overview/#design-your-model)を見てください！


## おさらいとアウトプット

それでは、最後に、今回学んだことをアウトプットするため、実際の使用をイメージして、テーブルを作成してみます！

- ブログを作って、記事を書く
- 記事にカテゴリを入れる
- 記事にコメントできるようにする

上記の三つを実現するためにテーブルを作成していきます。

**必要なテーブル**

- Category (カテゴリ）
- Article （記事）
- Comment (コメント)

**ポイント**

1. カテゴリを、記事に複数設定するため、ManyToManyを使用
2. 記事に複数のコメントを紐づけるため、コメントにForeignKeyを使用
3. 記事には、メインイメージを使用し、ステータスをIntegerで管理。削除は、フラグで実装する
4. Article にて Categoryを呼び出すため、Comment にて Article を呼び出すために、上記のような順番でテーブルを記述していきます。

上記の定義で、実際にテーブルを作成していきます！


``` models.py
from django.db import models


class Category(models.Model):
    
    name = models.CharField(max_length=31, verbose_name="カテゴリ名")

    def __str__(self):
        return self.name

    class Meta:

        verbose_name_plural = "カテゴリ"


class Article(models.Model):

    title = models.CharField(max_length=62, verbose_name="タイトル")
    slug = models.SlugField(verbose_name="URLスラッグ（英語）")
    image = models.ImageField(upload_to="article_image", blank=True, verbose_name="記事のイメージ写真", help_text="登録しない場合は、デフォルトのイメージ写真を使用する")
    categories = models.ManyToManyField(Category, verbose_name="カテゴリ")
    body = models.TextField(verbose_name="本文")
    status = models.PositiveSmallIntegerField(default=1, verbose_name="公開ステータス", help_text="1:下書き, 2:公開")
    liked = models.IntegerField(default=0, verbose_name="いいね数")
    is_deleted = models.BooleanField(default=False, verbose_name="削除フラグ")
    creator = models.CharField(max_length=31, verbose_name="投稿者"）
    created = models.DateTimeField(auto_now_add=True, verbose_name="投稿日時")
    modified = models.DateTimeField(auto_now=True, verbose_name="更新日")

    def __str__(self):
        return self.title

    class Meta:
        
        verbose_name_plural = "記事"
    


class Comment(models.Model):

    article = models.ForeignKey(Article, on_delete=models.CASCADE, verbose_name="記事", related_name="comments")
    commenter = models.CharField(max_length=31, verbose_name="コメント者名")
    body = models.TextField(verbose_name="コメント文")
    created = models.DateTimeField(auto_now_add=True, verbose_name="コメント投稿日時")

    def __str__(self):
        return self.article.title + ":{}のコメント".format(self.commenter)

    class Meta:

        verbose_name_plural = "コメント"

```


上記のmodels.pyだけで、カテゴリ、記事、コメントのDBテーブルの作成が完了です。
これだけで、早速、ブログやメディア発信などが簡単にできちゃいますね :) 


ということで、django 開発者への道① ~Modelsを理解する~ を終了します。

次回は、 URL（ルーティングについて、見ていきます！）

お疲れ様でした〜