設計の仕方

設計の仕方

models.pyのClassに決まった記述の仕方はありませんが、一般的な記述の仕方を覚えておくと、データベースの設計が楽になります。

from django.db import models
from django.core.validators import MaxValueValidator, MinValueValidator


class Category(models.Model):

    name = models.CharField(max_length=100, verbose_name="名前")
    created = models.DateTimeField(auto_now=True, verbose_name="作成日時")
    modified = models.DateTimeField(auto_now_add=True, verbose_name="更新日時")

    def __str__(self):
        return self.name

    class Meta:
        db_table = "category"
        verbose_name_plural = "記事カテゴリ"

class Article(models.Model):

    title = models.CharField(max_length=100, verbose_name="タイトル"
    body = models.TextField()
    category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True, verbose_name="カテゴリ")
    status = models.PositiveSmallIntegerField(default=1, verbose_name="公開ステータス", help_text="1:下書き 2:公開 3:削除", validators=[MinValueValidator(1), MaxValueValidator(2)])
    require_authentication = models.BooleanField(default=False, verbose_name=”ログインフラグ”, help_text="Trueの場合ログインしているユーザーしか閲覧できない")
    created = models.DateTimeField(auto_now=True, verbose_name="作成日時")
    modified = models.DateTimeField(auto_now_add=True, verbose_name="更新日時")

    def status2text(self):
        status2text = {1:"下書き", 2:"公開", 3:"削除"}
        return status2text[self.status]

    def __str__(self):
        return self.title

    class Meta:
        db_table = "article"
        verbose_name_plural = "記事"

上記がmodels.pyの一例です。テンプレ化しておきましょう。
ではそれぞれポイントを見ていきます。

verbose_name

全てのフィールドにはverbose_nameをつける癖をつけておくと良いです。
日本語でサービスを作る場合、フォームのラベルなどは日本語になりますし、他にもメリットはたくさんあります。
例えば、開発しているサービスを複数人で開発するようになったとき、他の誰が見てもそのフィールドの意味や役割を理解できるようになります。

Djangoのmodels.pyにはサービスのデータベースを理解する上で十分な情報が明示的に記載されます。つまり、データベースの定義書などがなくても、Djangoを理解できる人ならmodels.pyを見てデータベースの構成を理解することができます。
その上で各フィールドの名前を日本語にしておくことで理解しやすくできます。

help_text

verbose_nameと同じようにhelp_textも複数人での開発効率を上げるために必要です。
例えば、例のように記事のステータスを数字で管理するとき、それぞれの数字のステータスを理解できるようにhelp_textを書いておくと良いです。

verbose_name_plural

verbose_name_pluralは、フィールドではなく、テーブルの名前を変更することができます。admin.pyでテーブル情報を渡すことで、Djangoの管理画面でデータベースの各テーブルを一覧・管理できるようになります。
verbose_name_pluralを書いておくと、ここの一覧で全てのテーブルがその名前で表示され、日本語で書いておくことでどのテーブルかがわかりやすくなります。

status

一般的にデータのステータスが2つの場合はBooleanFieldが使用されます。TrueとFalseで2つのステータスを扱えるからです。

ステータスが2つより多い場合はIntegerFieldを使います。(1が下書き、2が公開、3が削除とルールを決めます。)
またここでは、少ない正の数で大丈夫なのでPositiveSmallIntegerFieldを使用しています。
さらにvalidatorsで最小と最大の数を指定することでそれ以外の数字が登録されるのを防ぐ処理を入れています。

最小の数はMinValueValidator、最大の数はMaxValueValidatorで指定することができます。

またstatusが3のデータは削除としています。
実際にデータを削除せず、statusを変えることで削除されたデータとして扱います。
こうすることで何かあったときでもArticleのデータを復元することが可能になります。

status2text

前回の記事で扱ったModelメソッドをしています。
数字のフィールドは扱いやすいのですがフロントエンドで扱うときに数字を文字列に変換する処理を入れる必要があったり色々と面倒です。

Modelメソッドを作成し、それぞれのステータスを文字列として返すメソッドを作っておくと良いでしょう。

require_authentication

require_authenticationではBooleanFieldを使ってTrue/Falseの型を指定しています。
こちらがTrueの場合は、ユーザーがログインしていないと閲覧できないようにする などのルールを設定することができます。
この場合、必要なステータスは2つしかないので、前述の通りBooleanFieldを使用しています。

タイムスタンプ

基本的に全てのデータには作成されたときの日時を入れておくべきです。
これを明示的にcreatedという名前をつけて設定しておくことが多いです。

またデータが更新・変更される可能性がある場合は、更新日時を入れておくためのmodifiedまたはupdatedを設定しておくことが多いです。

その他設定

__str__Metaは以前の記事で扱った通りです。
参照したときのデータをわかりやすくしておくために__str__を設定しておくべきです。
また基本的にClass名は大文字スタートで書きますが、データベース上の名前は全て小文字になるので、db_tableを書いて、明示的にテーブル名を書いておきます。

外部キー

Articleのcategoryで扱っているForeignKeyのon_deleteは注意が必要です。
基本的にはmodels.CASCADEで大丈夫ですが、例えばサービスを運用していて、カテゴリを削除した場合、そのカテゴリに紐付いている記事も自動で削除されてしまいます。

データはサービスにおいて命なので、誤って削除してしまう可能性も考慮すると、models.SET_NULLしておくと良いでしょう。
これだとカテゴリが削除されても、データは保持され、nullが値に代入されます。

他にもmodels.PROTECTなどがありますが、これを設定するとカテゴリ削除の際にエラーが出てそもそもカテゴリも削除できなくなります。利便性も考えるとSET_NULLがちょうど良いと思います。

最後に

Webサービスを開発するとき、ほとんど全ての機能にはデータベースが必要になってきます。
必要な機能を考えて、それぞれの機能を実装するために必要なデータを考えましょう。
そしてそれらのデータのお互いの関係性も考えておく必要があります。
参照しているのか、独立しているのか
参照している場合リレーションは 1対1なのか、1対多なのか、多対多なのか。
それに応じてmodesを記述していけると良いです。
データベースはプロダクト開発の手を進めるときの初めての工程であり、またここの設計次第でプロジェクト全体のコードの可読性やパフォーマンスが変わってきます。
また運用を進めていくと容易に変更することができなくなってきます。
たくさんのプロダクトを開発して、少しづつ覚えていけると良いでしょう。