【Django】モデルの基本:フィールドタイプとCRUD操作

Django

こんにちは、かつコーチです。

前回はマイグレーションの流れを解説しました。

今回はその前提となるモデル自体の書き方と、データの作成・取得・更新・削除、いわゆるCRUD操作の基本をまとめます。

モデルとは何か

Pythonのクラスがテーブルの設計図になる

モデルとは、データベースのテーブル構造をPythonのクラスとして表現したものです。

Djangoでは、SQLのCREATE TABLE文を直接書くのではなく、モデルクラスを定義することでテーブルの設計を管理します。

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

class Post(models.Model):
    title = models.CharField(max_length=200)
    content = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)

このクラスの1つのインスタンスが、テーブルの1行(レコード)に対応します。

よく使うフィールドタイプ

文字列系:CharFieldとTextField

短い文字列にはCharField、長い文章にはTextFieldを使います。

title = models.CharField(max_length=200)
content = models.TextField()

CharFieldmax_lengthの指定が必須です。

長さの上限を決めない自由記述にはTextFieldを使う、と覚えておけば迷いません。

数値・真偽値系:IntegerField・BooleanField

view_count = models.IntegerField(default=0)
is_published = models.BooleanField(default=False)

数値にはIntegerField、フラグ管理にはBooleanFieldを使います。

どちらもdefaultを指定しておくと、レコード作成時に値を省略できて便利です。

日時系:DateTimeFieldとauto_now系オプション

created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
  • auto_now_add=True:レコード作成時にのみ現在時刻を自動セットする
  • auto_now=True:保存されるたびに現在時刻で上書きする

「作成日時」にはauto_now_add、「更新日時」にはauto_now、と使い分けるのが定番です。

筆者は最初この2つを逆に指定してしまい、「作成日時のはずなのに、編集するたびに日付が変わってしまう」という不具合をリリース後に発見したことがあります。

似た名前のオプションなので、命名の意味をきちんと確認してから使うことをおすすめします。

選択肢を限定する:choices

決まった選択肢の中から選ばせたいフィールドには、choicesを指定します。

class Post(models.Model):
    STATUS_CHOICES = [
        ("draft", "下書き"),
        ("published", "公開"),
    ]
    status = models.CharField(max_length=10, choices=STATUS_CHOICES, default="draft")

こうしておくと、Django Adminのフォームが自動でドロップダウンになり、想定外の値が入力される事故を防げます。

CRUD操作の基本

CRUDとは、Create(作成)・Read(読取)・Update(更新)・Delete(削除)の頭文字を取った言葉です。

Djangoでは、これらをSQLを書かずにPythonのメソッド呼び出しだけで行えます。

Create:データを作成する

# 方法1:createメソッド
Post.objects.create(title="はじめての投稿", content="よろしくお願いします。")

# 方法2:インスタンスを作ってからsave()
post = Post(title="2つ目の投稿", content="今日も投稿します。")
post.save()

create()は「作成と保存を1行で行う」書き方、save()は「オブジェクトを組み立ててから保存する」書き方です。

保存前にいくつかの処理を挟みたい場合は、後者の書き方が向いています。

Read:データを取得する

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

# 条件を指定して取得(複数件)
published_posts = Post.objects.filter(status="published")

# 1件だけ取得
post = Post.objects.get(id=1)

filter()は条件に一致する複数件を返し、get()は条件に一致する1件だけを返します。

get()は該当が0件でも複数件でも例外を投げる点に注意が必要です。

Update:データを更新する

# 1件取得してから更新
post = Post.objects.get(id=1)
post.title = "タイトルを更新しました"
post.save()

# 条件に一致する複数件を一括更新
Post.objects.filter(status="draft").update(status="published")

1件だけ更新するなら「取得 → 値を変更 → save()」、複数件をまとめて更新するならupdate()を使うと効率的です。

Delete:データを削除する

post = Post.objects.get(id=1)
post.delete()

削除は取り消せない操作なので、本番環境で実行する前には対象件数をfilter()で確認する癖をつけておきましょう。

つまずきやすいポイント

get()で存在しないデータを検索して例外が発生する

❌ Before:存在確認をせずにget()だけを使う

def post_detail(request, post_id):
    post = Post.objects.get(id=post_id)
    return render(request, "blog/detail.html", {"post": post})

このコードは、URLに存在しないpost_idが指定されると、次の例外で画面全体がエラーになります。

django.core.exceptions.DoesNotExist: Post matching query does not exist.

筆者も削除済みの投稿へのリンクが残っていたページで、ユーザーがそのリンクを踏んでサーバーエラー画面を表示させてしまうという事態を経験しました。

✅ After:get_object_or_404を使う

from django.shortcuts import render, get_object_or_404

def post_detail(request, post_id):
    post = get_object_or_404(Post, id=post_id)
    return render(request, "blog/detail.html", {"post": post})

get_object_or_404()を使えば、該当データがない場合にサーバーエラーではなく、綺麗な「404 Not Found」画面を返してくれます。

ユーザーへの見え方も、開発者にとってのエラー原因の分かりやすさも、こちらのほうが優れています。

まとめ

この記事のポイント

  • モデルはPythonのクラスとして、テーブル構造とフィールドを定義する
  • CharFieldTextFieldIntegerFieldBooleanFieldなど、用途に合わせてフィールドタイプを選ぶ
  • CRUD操作はcreate()filter()get()update()delete()で行う
  • 単一データの取得で存在確認が必要な場面ではget_object_or_404()を使う

次に読むべき記事

  • ForeignKey・ManyToManyでリレーションを組む
  • QuerySetの基本とフィルタの書き方
  • フォーム(forms.Form/ModelForm)でバリデーションを実装する

タグ: Django, 初心者向け, データベース

タイトルとURLをコピーしました