1.

Django Model の定義方法完全ガイド — フィールド型と Meta

編集
この記事の要点
  • Django Model は django.db.models.Model を継承して定義。1 クラス = 1 テーブル
  • フィールドは CharField / IntegerField / BooleanField / DateTimeField / TextField / ForeignKey / ManyToManyField など 30 種以上
  • class Meta でテーブル名・順序・複合ユニーク制約などを定義
  • __str__() を必ず定義する。Django admin / shell の表示が劇的に分かりやすくなる
  • save() をオーバーライドすると保存前のフック処理が書ける(ハッシュ生成・スラグ自動生成等)
  • 抽象基底モデル abstract = True で共通フィールドを継承させると DRY に

最小のモデル

# myapp/models.py
from django.db import models

class User(models.Model):
    name = models.CharField(max_length=100)
    email = models.EmailField(unique=True)
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.name

このクラスで作成されるテーブルは myapp_user。主キー id は自動付与(BigAutoField)。

主要なフィールド型

フィールドSQL 相当主な用途
CharField(max_length=N)VARCHAR(N)短い文字列、max_length 必須
TextField()TEXT長文
IntegerField()INTEGER整数
BigIntegerField()BIGINT大きな整数
FloatField()REAL/DOUBLE浮動小数
DecimalField(max_digits, decimal_places)NUMERIC金額など正確な数値
BooleanField()BOOL真偽
DateField() / DateTimeField()DATE / TIMESTAMP日付・日時
EmailField()VARCHAR(254)email バリデーション付
URLField()VARCHAR(200)URL バリデーション付
SlugField()VARCHAR(50)URL 用スラグ
UUIDField()UUID/CHAR(32)UUID 主キー等
FileField() / ImageField()VARCHAR(100)ファイル・画像パス
JSONField()JSONB/JSON構造化データ
ForeignKey(Model, on_delete=...)FK + INDEX多対一
OneToOneField(Model, on_delete=...)FK + UNIQUE一対一
ManyToManyField(Model)中間テーブル多対多

フィールドの共通オプション

name = models.CharField(
    max_length=100,
    null=False,                     # DB レベルで NULL 不可(デフォルト)
    blank=False,                    # フォームバリデーションで空不可(デフォルト)
    default='匿名',                  # デフォルト値
    unique=True,                    # ユニーク制約
    db_index=True,                  # インデックス作成
    verbose_name='名前',             # 管理画面表示名
    help_text='本名で入力してください',
    choices=[                       # 選択肢
        ('M', '男性'),
        ('F', '女性'),
        ('O', 'その他'),
    ],
    editable=True,                  # admin で編集可
    validators=[],                  # カスタムバリデータ
)

リレーション

class Author(models.Model):
    name = models.CharField(max_length=100)

    def __str__(self):
        return self.name

class Book(models.Model):
    title = models.CharField(max_length=200)
    # 多対一: 1 著者は複数の本を持つ
    author = models.ForeignKey(
        Author,
        on_delete=models.CASCADE,    # 著者削除で本も削除
        related_name='books',        # author.books.all() でアクセス
    )
    # 多対多: 本は複数のタグを持つ
    tags = models.ManyToManyField('Tag', blank=True, related_name='books')

class Profile(models.Model):
    # 一対一: User とプロフィールは 1:1
    user = models.OneToOneField(
        'auth.User', on_delete=models.CASCADE, related_name='profile',
    )
    bio = models.TextField(blank=True)

class Tag(models.Model):
    name = models.SlugField(unique=True)

on_delete の主な値:

  • CASCADE — 参照先削除で自分も削除
  • PROTECT — 削除を防ぐ(参照があれば ProtectedError
  • SET_NULL — NULL にする(null=True 必須)
  • SET_DEFAULT — デフォルト値に
  • DO_NOTHING — 何もしない(DB 側で参照整合性違反になることも)

Meta クラス

class Order(models.Model):
    user = models.ForeignKey('User', on_delete=models.CASCADE)
    product_code = models.CharField(max_length=20)
    quantity = models.IntegerField()
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        db_table = 'orders'                 # テーブル名を指定
        ordering = ['-created_at']          # デフォルトソート順
        verbose_name = '注文'                # 管理画面の単数形
        verbose_name_plural = '注文一覧'      # 管理画面の複数形
        unique_together = [['user', 'product_code']]   # 複合ユニーク
        indexes = [
            models.Index(fields=['user', '-created_at']),
            models.Index(fields=['product_code']),
        ]
        constraints = [
            models.CheckConstraint(check=models.Q(quantity__gt=0), name='qty_positive'),
        ]
        permissions = [('can_cancel', 'Can cancel order')]
        abstract = False                    # True にすると抽象基底クラス

__str__ と save() オーバーライド

class Article(models.Model):
    title = models.CharField(max_length=200)
    slug = models.SlugField(unique=True, blank=True)
    body = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.title

    def save(self, *args, **kwargs):
        # slug を自動生成
        if not self.slug:
            from django.utils.text import slugify
            self.slug = slugify(self.title)
        # 親の save() を呼ぶ
        super().save(*args, **kwargs)

    def get_absolute_url(self):
        from django.urls import reverse
        return reverse('article-detail', kwargs={'slug': self.slug})

抽象基底モデル(共通フィールド)

class TimestampedModel(models.Model):
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        abstract = True       # ← これでテーブルは作られない

class Article(TimestampedModel):
    title = models.CharField(max_length=200)
    body = models.TextField()
    # created_at / updated_at は自動で生える

class Comment(TimestampedModel):
    article = models.ForeignKey(Article, on_delete=models.CASCADE)
    body = models.TextField()

カスタム Manager と QuerySet

class PublishedManager(models.Manager):
    def get_queryset(self):
        return super().get_queryset().filter(status='published')

class Article(models.Model):
    title = models.CharField(max_length=200)
    status = models.CharField(max_length=20, default='draft')

    objects = models.Manager()             # デフォルトマネージャ
    published = PublishedManager()         # カスタム

# 使い方
Article.objects.all()        # 全件
Article.published.all()      # 公開済のみ

マイグレーション適用

# モデル変更後
python manage.py makemigrations myapp
python manage.py migrate

# 状態確認
python manage.py showmigrations

# SQL を確認(実行しない)
python manage.py sqlmigrate myapp 0001

FAQ

Q: null=Trueblank=True の違い
A: null=True は DB 列で NULL 許容、blank=True は Django フォーム/admin で空欄 OK。文字列系null=True を付けず空文字を使うのが Django の慣習。

Q: 主キーを UUID にしたい
A: id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)。デフォルトの BigAutoField を上書き。

Q: モデル変更したのに DB が変わらない
A: makemigrations + migrate を必ずセットで。マイグレーションファイルが Git に入っていないと他環境に反映されない。

編集
Post Share
子ページ

子ページはありません

同階層のページ
  1. Model の定義方法
  2. マイグレーションファイルの作成
  3. テーブル定義の確認
  4. テーブルの作成
  5. テーブル名 = アプリケーション名 + モデル名の設定変更
  6. モデルの中身を確認