記事一覧へ戻る 本の順番で続きを読む

Django モデルとORM - データベース操作の基本

Python3上級 | 2026/02/18 21:20

Django モデルとORM

モデルの定義

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

class Category(models.Model):
    name = models.CharField('カテゴリ名', max_length=100)
    slug = models.SlugField(unique=True)
    order = models.IntegerField('表示順', default=0)

    class Meta:
        verbose_name = 'カテゴリ'
        verbose_name_plural = 'カテゴリ'
        ordering = ['order']

    def __str__(self):
        return self.name

class Post(models.Model):
    class Status(models.TextChoices):
        DRAFT = 'draft', '下書き'
        PUBLISHED = 'published', '公開'

    title = models.CharField('タイトル', max_length=200)
    slug = models.SlugField(unique=True)
    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, verbose_name='カテゴリ'
    )
    body = models.TextField('本文')
    status = models.CharField(
        '状態', max_length=10,
        choices=Status.choices, default=Status.DRAFT
    )
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        verbose_name = '記事'
        verbose_name_plural = '記事'
        ordering = ['-created_at']

    def __str__(self):
        return self.title

主要なフィールド型

フィールド 用途 主なオプション
CharField 短い文字列 max_length(必須)
TextField 長い文字列 -
IntegerField 整数 -
FloatField 浮動小数点 -
BooleanField 真偽値 default
DateTimeField 日時 auto_now, auto_now_add
ForeignKey 多対1リレーション on_delete(必須)
ManyToManyField 多対多リレーション -
OneToOneField 1対1リレーション on_delete(必須)
FileField ファイル upload_to
ImageField 画像 upload_to
SlugField URLスラグ unique
JSONField JSON default=dict

クエリセット(ORM操作)

from blog.models import Post, Category

# 作成
post = Post.objects.create(
    title='最初の記事',
    slug='first-post',
    author=user,
    body='記事の本文です。',
    status='published'
)

# 全件取得
posts = Post.objects.all()

# フィルタリング
published = Post.objects.filter(status='published')
recent = Post.objects.filter(created_at__gte='2026-01-01')
by_author = Post.objects.filter(author__username='admin')

# 除外
not_draft = Post.objects.exclude(status='draft')

# 1件取得
post = Post.objects.get(slug='first-post')  # なければDoesNotExist
post = Post.objects.filter(slug='first-post').first()  # なければNone

# チェイン
posts = Post.objects.filter(
    status='published'
).order_by('-created_at')[:10]

# 集約
from django.db.models import Count, Avg
category_counts = Category.objects.annotate(
    post_count=Count('post')
).order_by('-post_count')

# Q オブジェクト(複雑な条件)
from django.db.models import Q
results = Post.objects.filter(
    Q(title__icontains='Python') | Q(body__icontains='Python')
)

リレーション操作

# 正引き(ForeignKey先を取得)
post = Post.objects.get(id=1)
print(post.category.name)

# 逆引き(related_name)
user = User.objects.get(username='admin')
user_posts = user.posts.all()  # related_name='posts'

# select_related(JOIN、ForeignKeyの最適化)
posts = Post.objects.select_related('author', 'category').all()

# prefetch_related(ManyToManyや逆引きの最適化)
categories = Category.objects.prefetch_related('post_set').all()

マイグレーション

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

# 適用
python manage.py migrate

# 状態確認
python manage.py showmigrations

# SQLを確認
python manage.py sqlmigrate blog 0001

管理画面

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

@admin.register(Category)
class CategoryAdmin(admin.ModelAdmin):
    list_display = ['name', 'slug', 'order']
    prepopulated_fields = {'slug': ('name',)}

@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
    list_display = ['title', 'author', 'status', 'created_at']
    list_filter = ['status', 'category', 'created_at']
    search_fields = ['title', 'body']
    prepopulated_fields = {'slug': ('title',)}
    date_hierarchy = 'created_at'

まとめ

  • モデルクラスでテーブル構造を定義
  • objects.filter() / exclude() / get() でデータを取得
  • select_related / prefetch_related でN+1問題を防止
  • makemigrations / migrate でスキーマ変更を管理
  • admin.py で管理画面をカスタマイズ