この内容は古いバージョンです。最新バージョンを表示するには、戻るボタンを押してください。
バージョン:4
ページ更新者:guest
更新日時:2026-06-11 07:29:05

タイトル: テーブルの作成
SEOタイトル: Django モデルでのテーブル作成完全ガイド(CharField / ForeignKey / migrate)

この記事の要点
  • Django は models.py に Python クラスを書くだけで DB テーブルを定義できる(ORM)
  • class Post(models.Model): を継承し、フィールド型(CharField / TextField / IntegerField / DateTimeField / ForeignKey 等)を定義
  • 主キー id は自動生成(BigAutoField)。Meta クラスで db_table / ordering / unique_together などを指定
  • 反映手順: makemigrations でマイグレーションファイル生成 → migrate で DB に適用
  • ForeignKey(多対1)/ ManyToManyField(多対多)/ OneToOneField(1対1)でリレーション定義

Django モデルとテーブルの関係

Django ORM では models.py に書いた Python クラスが、そのまま 1 つの DB テーブルに対応します。フィールド型は SQL の DDL に自動変換され、マイグレーション機能で世代管理されます。スキーマ変更も全部 Python コードで管理できるのが強み。

Python クラスDB テーブル
class Post(models.Model)blog_post テーブル
title = CharField(max_length=200)title VARCHAR(200)
created_at = DateTimeField()created_at DATETIME
author = ForeignKey(User)author_id BIGINT + FK 制約

基本のモデル定義

# blog/models.py
from django.db import models
from django.contrib.auth.models import User

class Category(models.Model):
    name = models.CharField('カテゴリ名', max_length=50, unique=True)
    slug = models.SlugField(max_length=50, unique=True)

    class Meta:
        db_table = 'blog_category'
        verbose_name = 'カテゴリ'
        verbose_name_plural = 'カテゴリ一覧'
        ordering = ['name']

    def __str__(self):
        return self.name


class Post(models.Model):
    STATUS_CHOICES = [
        ('draft', '下書き'),
        ('published', '公開'),
        ('archived', 'アーカイブ'),
    ]

    title = models.CharField('タイトル', max_length=200)
    body = models.TextField('本文')
    status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='draft')
    view_count = models.PositiveIntegerField(default=0)

    author = models.ForeignKey(
        User,
        on_delete=models.CASCADE,
        related_name='posts',
        verbose_name='著者',
    )
    category = models.ForeignKey(
        Category,
        on_delete=models.SET_NULL,
        null=True, blank=True,
        related_name='posts',
    )
    tags = models.ManyToManyField('Tag', blank=True, related_name='posts')

    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    published_at = models.DateTimeField(null=True, blank=True)

    class Meta:
        db_table = 'blog_post'
        ordering = ['-created_at']
        indexes = [
            models.Index(fields=['status', '-created_at']),
            models.Index(fields=['author', 'status']),
        ]
        constraints = [
            models.UniqueConstraint(
                fields=['author', 'title'],
                name='unique_author_title',
            ),
        ]

    def __str__(self):
        return self.title


class Tag(models.Model):
    name = models.CharField(max_length=30, unique=True)
    def __str__(self):
        return self.name

主なフィールド型

Django フィールド用途SQL 型 (PostgreSQL 例)
CharField(max_length=N)短い文字列(必須: max_length)VARCHAR(N)
TextField()長文TEXT
SlugField(max_length=50)URL 用識別子VARCHAR(50)
EmailField()メールアドレス(バリデーション付き)VARCHAR(254)
URLField()URLVARCHAR(200)
IntegerField()整数INTEGER
BigIntegerField()大きな整数BIGINT
PositiveIntegerField()0 以上の整数INTEGER CHECK (>= 0)
FloatField()浮動小数点DOUBLE PRECISION
DecimalField(max_digits, decimal_places)金額・正確な小数NUMERIC
BooleanField()真偽値BOOLEAN
DateField()日付DATE
DateTimeField()日時TIMESTAMP
TimeField()時刻TIME
JSONField()JSON(Django 3.1+)JSONB (PostgreSQL)
FileField(upload_to=...)ファイルアップロードVARCHAR (パス)
ImageField(upload_to=...)画像(Pillow 必須)VARCHAR (パス)
UUIDField(default=uuid.uuid4)UUID 主キーUUID

リレーション 3 種

種類フィールド
多対 1ForeignKey記事 N - 著者 1
1 対 1OneToOneFieldUser - Profile
多対多ManyToManyField記事 N - タグ N

on_delete の選択肢

# author が削除されたときの挙動
author = models.ForeignKey(User, on_delete=models.CASCADE)
# CASCADE       - 関連も一緒に削除
# PROTECT       - 関連があれば削除をエラーに
# SET_NULL      - NULL にする (null=True 必須)
# SET_DEFAULT   - デフォルト値にする (default 必須)
# SET(関数)     - 任意の関数で値設定
# DO_NOTHING    - 何もしない (SQL レベルの FK 制約による)

多対多の through テーブル

# 追加情報(タグ付与日など)を中間テーブルに持たせたい場合
class Post(models.Model):
    tags = models.ManyToManyField('Tag', through='PostTag')

class PostTag(models.Model):
    post = models.ForeignKey(Post, on_delete=models.CASCADE)
    tag = models.ForeignKey('Tag', on_delete=models.CASCADE)
    added_at = models.DateTimeField(auto_now_add=True)
    weight = models.IntegerField(default=1)

    class Meta:
        unique_together = ('post', 'tag')

マイグレーションの実行

# 1. マイグレーションファイル生成
python manage.py makemigrations blog

# blog/migrations/0001_initial.py が生成される

# 2. 生成された SQL を確認(実行はしない)
python manage.py sqlmigrate blog 0001

# 3. DB に反映
python manage.py migrate

# 4. 適用状態の確認
python manage.py showmigrations

# 5. ロールバック(前の世代に戻す)
python manage.py migrate blog 0001

# 6. ゼロまで戻す
python manage.py migrate blog zero

admin.py に登録

# blog/admin.py
from django.contrib import admin
from .models import Post, Category, Tag, PostTag

class PostTagInline(admin.TabularInline):
    model = PostTag
    extra = 1

@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
    list_display = ('id', 'title', 'author', 'category', 'status', 'created_at')
    list_filter = ('status', 'category', 'created_at')
    search_fields = ('title', 'body')
    raw_id_fields = ('author',)
    inlines = [PostTagInline]
    date_hierarchy = 'created_at'

admin.site.register(Category)
admin.site.register(Tag)

主キー(id)の挙動

明示しなければ id BIGAUTO が自動で追加されます(Django 3.2+)。UUID 主キーにしたいときは:

import uuid
class Post(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    title = models.CharField(max_length=200)

FAQ

Q: 既存 DB に Django を載せたい(マイグレーション不要)
A: python manage.py inspectdb > models.py で既存テーブルから models を逆生成。各モデルで class Meta: managed = False を付けると Django が DDL に介入しなくなります。

Q: マイグレーションファイルをやり直したい
A: 開発初期なら migrations/ 内の 0001_initial.py 以降を削除 + DB をリセット + 再 makemigrations。本番では絶対やらない。

Q: フィールド追加で「default を聞かれる」
A: 既存レコードに対する初期値が必要。default= を付ける、null=True を付ける、または対話プロンプトで一時値を指定します。