1-8. モデルの作成

今回のテーマは「モデルの作成」です。ここまで非常に初歩ですが、ViewとTemplateについて見てきました。残りはModelですね。Djangoではモデルに様々な情報をもたせることでデータベースとの連携やバリデーション処理をスマートに行える仕組みを持っています。今回は掲示板ということで簡単なモデルを作って学習することにしましょう。 ※本ページはTemplateViewでテンプレートを表示するまで読まれた方を対象としています。そのためサンプルソースコードが省略されている場合があります。


threadアプリケーションの追加

まずはthreadアプリケーションを作ります。これは掲示板のスレッドに関することを処理するアプリケーションです。

(venv)$ ./manage startapp thread

baseアプリケーションと同様にthread/urls.pyの追加、mysite/settings.pyにアプリケーションの追加、mysite/urls.pyにURLの追加を行います。この辺りはbaseアプリケーションと同様です。

mysite/settings.py(修正箇所のみ)

  INSTALLED_APPS = [
      'django.contrib.admin',
      'django.contrib.auth',
      'django.contrib.contenttypes',
      'django.contrib.sessions',
      'django.contrib.messages',
      'django.contrib.staticfiles',
      'django.contrib.sites',
      'django.contrib.sitemaps',
      'debug_toolbar',
      'base',
+     'thread',
  ]

mysite/urls.py(終始箇所のみ)

  urlpatterns = [
      path('admin/', admin.site.urls),
      path('', include('base.urls')),
+     path('thread/', include('thread.urls')),
  ]

thread/urls.py

from django.urls import path

from . import views
app_name = 'thread'

urlpatterns = [
]

これでアプリケーションの追加処理はOKです。

モデルの追加

threadアプリケーションのモデルは以下の様なものを想定します。まずは掲示板の最低限の機能を有するように以下のようなモデルを考えます。

  • Topic: ID、タイトル、本文、ユーザー名、作成日、更新日
  • Comment: ID、本文、ユーザー、作成日、公開フラグ
  • Category: ID、タイトル、ソート番号、URLコード

まずはソース全体がどうなるかを見てみましょう。thread/models.pyは以下のようになります。

thread/models.py

from django.db import models
from django.contrib.auth.models import User

# トピックのステータス一覧
TOPIC_STATUS_CHOICE = (
    (0, '公開'),
    (1, '非公開'),
    (2, '承認待ち'),
)

# トピックのステータス一覧
COMMENT_STATUS_CHOICE = (
    (0, '公開'),
    (1, '非公開'),
)

class TopicManager(models.Manager):
    # Topic操作に関する処理を追加
    pass

class CommentManager(models.Manager):
    # Comment操作に関する処理を追加
    pass

class CategoryManager(models.Manager):
    # Category操作に関する処理を追加
    pass

class Category(models.Model):
    name = models.CharField(
        'カテゴリー名',
        max_length=50,
    )
    url_code = models.CharField(
        'URLコード',
        max_length=50,
        null=True,
        blank=False,
        unique=True,
    )
    sort=models.IntegerField(
        verbose_name='ソート',
        default=0,
    )
    objects = CategoryManager()

    def __str__(self):
        return self.name

class Topic(models.Model):
    user_name = models.CharField(
        '名無しさん',
        max_length=30,
        null=True,
        blank=False,
    )
    title = models.CharField(
        'タイトル',
        max_length=255,
        null = False,
        blank = False,
    )
    message = models.TextField(
        verbose_name='本文',
        null=True,
        blank=False,
    )
    category = models.ForeignKey(
        Category,
        verbose_name='カテゴリー',
        on_delete=models.PROTECT,
        null=True,
        blank=False,
    )
    status = models.IntegerField(
        'トピックのステータス',
        default=0,
        choices=TOPIC_STATUS_CHOICE,
    )
    created = models.DateTimeField(
        auto_now_add=True,
    )
    modified = models.DateTimeField(
        auto_now=True,
    )
    objects = TopicManager()

    def __str__(self):
        return self.title

class Comment(models.Model):
    id = models.BigAutoField(
        primary_key=True,
    )
    no = models.IntegerField(
        default=0,
    )
    user_name = models.CharField(
        '名無しさん',
        max_length=30,
        null=True,
        blank=False,
    )
    topic = models.ForeignKey(
        Topic,
        on_delete=models.PROTECT,
    )
    message = models.TextField(
        verbose_name='投稿内容'
    )
    status = models.IntegerField(
        'コメントのステータス',
        default=0,
        choices=COMMENT_STATUS_CHOICE,
    )
    created = models.DateTimeField(
        auto_now_add=True,
    )
    objects = CommentManager()

    def __str__(self):
        return '{}-{}'.format(self.topic.id, self.no)

簡単な解説

モデルフィールド

モデルの各クラスのプロパティはモデルフィールドで規定していきます。公式のモデルフィールドリファレンスにモデルフィールドの種類がまとまっています。Djangoではモデルが持つ関連情報はできるだけ一箇所にまとめることを理想としていて、データベース格納時のデータ型、リレーションに関する情報、デフォルト値、nullの許容等の情報を付与していきます。多くのWeb開発ではAPIの設計、データベース設計を終えた後に実装に入ると思いますので、設計を素直にモデルに書き起こしていく作業となります。

nullとblank

nullとblankが出てきました。nullはデータベース上でNULLを許容するかどうか?であり、blankはユーザーが入力項目として必須項目にするかどうかということです。具体的にはblankにFalseを当てるとフォームでインプットタグにrequiredが付きます。

IDの自動生成

さて、IDに関する記述がないのに気づきましたか?IDに関しては明記しない場合はDjangoによって自動生成されます。その場合はデータ型はintとなりPrimary Keyが設定されます。

外部キー

今回、1対多の関係を構築するためにトピックはカテゴリーのidをコメントはトピックのidを保有しています。このような場合DjangoではForeignKeyを用います。その場合、外部キーとして保有してるモデルが削除された場合の削除方法をon_deleteで指定します。種類としては以下のようなものがあります。

  • models.CASCADE : 外部キーのモデルと一緒に参照しているモデルも削除される
  • models.PROTECT : 保護される(参照されている場合には削除できない)
  • models.SET_NULL : 外部キーのモデルが削除された場合IDはNULLとなる
  • models.SET_DEFAULT : デフォルトで設定されている値がセットされる。

オススメはPROTECTでしょうか。CASCADEは少々リスクが高いですが、設計次第では問題ないと思います。

マネージャーについて

また、今回はTopicManagerとCommentManager,CategoryManagerを用意し、各モデルのobjectsと紐づけました。今後、各モデルに関する操作に関わる処理はこのマネージャーに追加していき、ビューからはその処理を呼び出し、モデルに作用するようにします。慣れない内はViewで多くの処理をしてしまい肥大化しがちですが、モデルが担うビジネスロジックの処理はモデルで行うべきかと(自戒をこめて)考えています。

最後に

解説が駆け足となってしまい、説明が不十分な点もあるかと思いますが、まずはDjangoのモデルがどういう性質のものかを体感してもらうことが大事かと思います。次回は作成したモデルを使っていきますよ。

Sponsored Link


「1-8. モデルの作成」への2件のフィードバック

  1. objects = CategoryManager
    この部分ですが
    objects = CategoryManager()
    じゃなくていいのでしょうか?

    1. 名無しさん
      ご指摘ありがとうございます。
      objects = CategoryManager()が正しいです。

      記事修正いたしました。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です