Lecture 04: DRF Architecture & Django Model Design
← DRF × Vue.js 実践講座:まとめページに戻る | ← 第三講:プロジェクト初期セットアップとパッケージ導入
はじめに:いよいよ「第二部:バックエンド開発編」がスタート!
第三講では、フロントエンド(Vue 3 + Vite + TypeScript)の環境構築やパッケージ導入、そしてPython仮想環境(venv)の構築とDjangoプロジェクト・アプリの骨格作成を行いました。
今回から、講座は 「第二部:バックエンド開発編(Django REST Framework)」 へと突入します!
これから実際にバックエンドを構築していきますが、「いきなり何となくコードを書き始める」のは挫折の元です。
DRF(Django REST Framework)は非常に強力な反面、「シリアライザは何のためにあるのか」「リクエストはどんな順序で処理されてJSONになるのか」という全体のメンタルモデル(地図)が頭に入っていないと、コードの意図を見失ってしまいます。
そこで本講では、
- DRF の全体構造とリクエスト処理シーケンス(時系列の流れ) を図解で徹底整理
- データ設計の前提となる オブジェクト指向(クラス・継承・インスタンス) の基本を整理
- 実際にコードを書いて Django モデル(ORM)の定義とデータ設計アーキテクチャ(フィールド・オプション・クラスメタ) を完全網羅
という3段階で、APIの全体像の把握からデータベースの基礎設計・モデル実装までを一気に進めていきます!
1. REST API の基本動作(REST API IN ACTION)
まずは、すべての土台となる 「REST API の基本動作」 を確認しましょう。
■ クライアントとサーバーのやりとり
- REST clients(クライアント): PCブラウザ(Vue.js / Axios)やスマホアプリなど、APIを呼び出す側。
- REST server(サーバー): データを管理し、要求に応じて処理を行う側(Django / DRF)。
■ HTTPメソッド(動詞)による操作
クライアントは、目的のリソース(URL)に対して HTTPメソッド を指定してリクエストを送ります。
| HTTPメソッド | 役割 | CRUD対応 | URLの例 |
|---|---|---|---|
| GET | リソースの取得 | Read(読取) |
GET /api/messages/(一覧取得) |
| POST | 新規作成 | Create(作成) |
POST /api/messages/(新規投稿) |
| PUT / PATCH | 更新 | Update(更新) |
PATCH /api/messages/1/(一部更新) |
| DELETE | 削除 | Delete(削除) |
DELETE /api/messages/1/(削除) |
サーバーはHTMLではなく、純粋なデータ形式である JSON を返します。これにより、クライアントがブラウザ(Vue)でもスマホアプリでも、同じAPIをそのまま共通利用できます。
2. Django と DRF の関係性(なぜ Django に DRF を乗せるのか)
続いて、私たちが開発している 「DRF プロジェクト」 の位置づけです。
「Django」と「DRF」は別個のフレームワークではなく、下図のように協力関係にあります。
「堅牢なWeb基盤である Django」の上に、「REST API に特化した便利機能を詰め込んだ DRF」を拡張として乗せている のがポイントです。
-
Django の役割(土台):
- ORM(データベース操作・マイグレーション): SQLを書かずにPythonクラスでDBを定義・操作。
- セキュリティ基盤: SQLインジェクションやCSRFなどの脅威からアプリを防御。
-
設定管理:
settings.pyやurls.pyによる全体統括。
-
DRF の役割(API特化機能):
- シリアライザ(Serializer): JSON と Python オブジェクトの相互変換・入力バリデーション。
- ビューセット(ViewSet): CRUD API を最小限のコードで一括実装。
- 認証・権限(Token / Permissions): JWTやトークンを用いたAPI認証とアクセス制御。
- ブラウサブルAPI: ブラウザから直接APIをテストできるGUI。
面倒なDB接続やセキュリティはDjangoに任せ、API特有の処理(シリアライズやルーティング)はDRFの武器を使ってスマートに解決します。
3. DRF の全体像(アーキテクチャ)
では、Django / DRF の内部はどのような構造になっているのでしょうか。全体のアーキテクチャを見てみましょう。
従来の Django と DRF の最大の違いは、ビュー(View)の右側の構造です。
【従来の Django (MTV)】
ビュー ⇄ フォーム(検証) ➔ モデル ⇄ DB ➔ テンプレート(HTML組み立て) ➔ HTML返却
【DRF(本講座)】
DRF用ビュー ⇄ シリアライザ(検証・JSON変換) ⇄ モデル ⇄ DB ➔ JSON返却
- 従来の Django: フォームで入力検証し、DBのデータをテンプレート(HTML)に埋め込んでブラウザに返します。
- DRF: テンプレートやフォームは使わず、シリアライザ(Serializer) が「入力値の検証」と「JSON形式への組み立て」を一手に引き受けます。
4. 1リクエストの処理シーケンス(時系列で追うデータの流れ)
静的な構造(配置)を押さえたところで、「実際にブラウザからデータが送られた時、内部で各パーツがどんな順番で呼び出されるのか」 を時系列(シーケンス)で追ってみましょう!
下図は、ブラウザから POST /api/locations/(新規作成リクエスト)が送られた場合の完全な処理シーケンスです。
上図の通り、リクエストは 6つのステップ を順番に通過してレスポンスへと変換されます。今後のコーディングで実際に書くコードと直結する超重要フローです。
1) 入口の検問(Middleware)
-
ブラウザ ➔ ミドルウェア:
POST /api/locations/(Cookie や X-CSRFToken などを含む)が届きます。 -
ミドルウェアでの検証:
- CORS 検証: Vue.js(フロント)からのオリジン間通信を許可するかチェック。
- Session / CSRF / 認証検証: 送られてきたトークンやヘッダーを検証し、リクエストの正当性を確認。不正な通信はこの時点で弾かれます。
2) ルーティング(URLconf)
-
ミドルウェア ➔ URLconf (
urls.py): 検問を通過したリクエストの URL(/api/locations/)を照合。 -
URLconf ➔ ViewSet:
- URL と HTTP メソッド(POST)から、呼び出すべきビューのメソッド(例:
LocationViewSet.create())を決定し、処理をディスパッチ(転送)します。
- URL と HTTP メソッド(POST)から、呼び出すべきビューのメソッド(例:
3) 入力の検証:JSON ➔ Python(Serializer: Input)
ここからが DRF の中核処理です。
-
ViewSet ➔ Serializer: クライアントから届いた生データ(
data=request.data)をシリアライザに渡します。 -
シリアライザ内部でのバリデーション:
-
is_valid()およびvalidate()が実行されます。 - 型チェックや必須チェック、カスタムルール検証を行い、生の JSON データを安全な Python の型付きデータ に変換します。
-
4) 保存:Python ➔ SQL(ORM)
-
ViewSet ➔ ORM: バリデーションを通過したら、
serializer.save()を呼び出します。 -
ORM ➔ SQL Server(データベース):
- Django ORM が自動的に
INSERTまたはUPDATEの SQL クエリを発行。 - データベースにレコードが書き込まれ、完了通知(
OK)が戻ります。
- Django ORM が自動的に
5) 出力:Python ➔ JSON(Serializer: Output)
-
ViewSet ➔ Serializer: 保存されたモデルインスタンス(Pythonオブジェクト)を再びシリアライザに渡します(
instance -> serializer.data)。 -
シリアライザ ➔ ViewSet:
- Python のモデルオブジェクトを、クライアント(Vue)が受け取れる JSON 形式の辞書データ(JSON dict) に組み立て直します。
6) 応答(Response)
-
ViewSet ➔ ブラウザ:
- シリアライザが組み立てた JSON データとともに、適切な HTTP ステータスコード(新規作成時は
201 Created、取得時は200 OK)を返却します。
- シリアライザが組み立てた JSON データとともに、適切な HTTP ステータスコード(新規作成時は
- ブラウザ側の Vue 3(Axios)がこの JSON を受け取り、画面上に新しいデータが即座に反映されます!
5. settings.py へのアプリ登録(モデル作成の準備)
モデル(データベースのテーブル)を定義する前に、まずは Django プロジェクト(project/settings.py)に、私たちが作成したアプリ message と、インストールした rest_framework を認識させる必要があります。
backend/project/settings.py を開き、INSTALLED_APPS のリストに以下のように追記します。
INSTALLED_APPS = [
# Django 標準アプリ
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
# サードパーティ製パッケージ
'rest_framework', # DRF を有効化
# 作成した自作アプリ
'message', # メッセージアプリを登録
]
ここに登録しておくことで、Django は「message アプリのフォルダ内にある models.py を読み込んでテーブルを作ればいいんだな」と認識できるようになります。
6. Django モデルの基本概念と書き方
■ そもそも「オブジェクト指向」とは?(Djangoモデルを理解するための前提知識)
Django のモデル(Model)を触る前に、必ず押さえておきたいのが 「オブジェクト指向(Object-Oriented Programming: OOP)」 の考え方です。
「なぜクラスを書くのか?」「なぜ (models.Model) とカッコをつけて継承するのか?」という疑問は、オブジェクト指向の基本を押さえることですべて綺麗に繋がります。
オブジェクト指向とは、「現実世界のモノや概念をプログラム内の『オブジェクト(モノ)』として捉え、データ(属性)と振る舞い(メソッド)をひとまとめにして扱う考え方」 です。
下図のように、たとえば「車」というオブジェクトで考えてみましょう。
- 属性(プロパティ): 色(青)、車種(SUV)、速度、燃料など、そのモノの「状態を表すデータ」
- メソッド: 走る()、止まる()、曲がる()、給油する()など、そのモノができる「振る舞い(動作)」
データと動作をバラバラに管理するのではなく、ひとつの「部品」としてまとめることで、プログラムの見通しが良くなり、再利用やメンテナンスが格段にしやすくなる のがオブジェクト指向の大きなメリットです。
■ 「クラス」「継承」「インスタンス」の3ステップ
では、プログラム上ではこのオブジェクトをどう組み立てていくのでしょうか。
基本は以下の 3つのステップ で捉えます。
-
① クラス(設計図):
- まだ実体のない「型・設計図」です。社員を表す
Employeeという枠組みを作り、保持するデータ(氏名・部署・社員番号)や処理(自己紹介など)を定義します。
- まだ実体のない「型・設計図」です。社員を表す
-
② 継承(機能の引き継ぎ):
- すでにあるクラス(親)の内容をすべて引き継ぎ、新しい機能だけをプラスして別のクラス(子)を作る仕組み です。
- たとえば管理職
Managerクラスを作るとき、ゼロから作り直すのではなくEmployeeを「継承」すれば、社員としてのデータや処理をそのまま受け継いだ上で、新しく「承認する(approve())」というメソッドだけを追加できます。
-
③ インスタンス(実体化):
- 設計図(クラス)をもとに、メモリ上に作られた具体的なデータ(実体) です。
- 同じ
Managerという設計図から、山田さん(manager1)や佐藤さん(manager2)といった個別の実体を何人でも生み出すことができます。
■ コードで見るオブジェクト指向
この「クラス ➔ 継承 ➔ インスタンス」を Python のコードに落とし込むと、次のようになります。
# ① クラス(設計図):一般社員を定義
class Employee:
def __init__(self, name, department, employee_id):
self.name = name # 属性(プロパティ)
self.department = department # 属性(プロパティ)
self.employee_id = employee_id # 属性(プロパティ)
def introduce(self): # メソッド(振る舞い)
return f"{self.department}の{self.name}(社員番号: {self.employee_id})です。"
# ② 継承:Employee を引き継ぎ、Manager クラスを作成
class Manager(Employee):
def approve(self): # Manager 独自のメソッドを追加
return f"{self.name}が申請を承認しました。"
# ③ インスタンス化:設計図から具体的な「実体」を2名作成
manager1 = Manager("山田 太郎", "システム課", "E001")
manager2 = Manager("佐藤 花子", "開発課", "E002")
print(manager1.introduce()) # 親(Employee)から引き継いだメソッドが呼べる
print(manager1.approve()) # 子(Manager)で追加したメソッドが呼べる
■ これが Django モデル(ORM)とどう繋がるのか?
この Python のオブジェクト指向の仕組みが、そのまま Django のデータベース設計 に直結しています。
| オブジェクト指向(Python) | 意味・役割 | Django モデルでの対応 | データベース(SQL)の世界 |
|---|---|---|---|
| クラス(設計図) | モノの定義・枠組み | モデル(class Message) |
テーブル(表) |
| プロパティ(属性) | モノが持つデータ | フィールド(title, content) |
カラム(列) |
| メソッド(振る舞い) | モノの処理・動作 | __str__() や save() などのフック |
(アプリ側の処理ロジック) |
| 継承(引き継ぎ) | 親の機能を全引き継ぎ | models.Model の継承 |
ORM の全機能(.save() や .all()) |
| インスタンス(実体) | 具体的なデータ1件 | モデルインスタンス(msg = Message(...)) |
レコード(1行のデータ) |
「なぜ Django では class Message(models.Model): と書くのか?」
その理由は、Django があらかじめ用意してくれている基底クラス models.Model を「継承」するため です。
models.Model を継承するだけで、自分で面倒な SQL を書かなくても、DBへの保存(.save())や検索(objects.all())といった強力な ORM メソッドが最初から手に入ります。
■ モデルの基本的な書き方(構文)
それでは、backend/message/models.py を開いてみましょう。初期状態では空の from django.db import models だけが書かれています。
ここに、メッセージを管理する Message モデルを以下のように定義します。
from django.db import models
class Message(models.Model):
"""
メッセージを管理するテーブル
"""
title = models.CharField(max_length=100, verbose_name="タイトル")
content = models.TextField(verbose_name="本文")
is_read = models.BooleanField(default=False, verbose_name="既読フラグ")
created_at = models.DateTimeField(auto_now_add=True, verbose_name="作成日時")
updated_at = models.DateTimeField(auto_now=True, verbose_name="更新日時")
def __str__(self):
return self.title
■ コードの重要ポイント解説
-
models.Modelを継承する(★継承の力):-
class Message(models.Model):と書くことで、Django が提供する基底クラスmodels.Modelの全機能を継承しています。 - これにより、自分でDB保存やSQL発行の処理を1行も書かなくても、
.save()(保存)や.delete()(削除)、Message.objects.all()(一覧取得)といった超強力な ORM メソッドが最初から自由に使えるようになります。
-
-
主キー(
id)は自動生成される:- コード上には
idを書いていませんが、Django が自動的に主キー(プライマリキー)としてid = models.BigAutoField(primary_key=True)を裏側で追加してくれます。
- コード上には
-
__str__(self)メソッドを定義する(★振る舞いの定義):- Python の特殊メソッドで、そのインスタンス(オブジェクト)を文字列として表現したときの値を返します。
- これを書いておくと、Django の管理画面やターミナルでデータを参照したときに
<Message: Message object (1)>ではなく、メッセージのtitle(例:"こんにちは")が表示されるようになり、開発効率が劇的に上がります。
7. フィールドクラス(Field Classes)一覧と特徴
モデルの各カラムを定義する際に使うのが、Django が提供する フィールドクラス(Field クラス) です。
フィールドクラスを指定することで、「そのカラムがどんな型のデータを保持するのか」「どんな制約をかけるのか」を決定します。
代表的なフィールドクラスと、データベース上のカラム型、役割は以下の通りです。
| Field クラス | カラムの型 (SQL) | 説明・主な用途 |
|---|---|---|
BooleanField |
bool |
True / False の真偽値を扱う(完了フラグ、公開フラグなど) |
CharField |
varchar(n) |
短〜中程度の文字列を扱う。文字数制限(max_length)が必須(タイトル、氏名など) |
TextField |
text |
文字数制限を設ける必要のない長い文章を扱う(記事本文、コメントなど) |
UUIDField |
char(32) |
推測されにくい一意な識別子(uuid.UUID 型)を扱う |
EmailField |
varchar(254) |
メールアドレスを扱う(裏側でメールアドレスの形式バリデーションが働く) |
IntegerField |
integer |
整数値(int 型)を扱う(数量、年齢、スコアなど) |
FloatField |
real |
浮動小数点数(float 型)を扱う(価格、座標、重みなど) |
DateField |
date |
日付(datetime.date 型)のみを扱う(生年月日、イベント開催日など) |
DateTimeField |
datetime |
日時(datetime.datetime 型)を扱う(作成日時、最終ログイン日時など) |
FileField |
varchar(100) |
画像やPDFなどのメディアファイルを扱う(実ファイルはストレージへ保存し、DBにはパスを保存) |
8. フィールドオプション(Field Options)一覧
各フィールドクラスのカッコ () の中に指定する引数が フィールドオプション です。
データベースのカラム制約(NOT NULL や UNIQUE など)や、DRF のシリアライザで機能するバリデーションルールを細かくコントロールできます。
実務で必ず押さえておくべき主要オプションは以下の通りです。
| フィールドオプション | 説明 | 役割・活用例 |
|---|---|---|
verbose_name |
フィールドの名前 | Django 管理画面などで表示される人間向けの表示名(例: verbose_name="タイトル") |
null |
データベースの NOT NULL 制約 | デフォルトは False(NULL を許可しない)。null=True で DB 上の NULL を許可 |
unique |
データベースのユニーク制約 |
True にすると、テーブル全体で値の重複を禁止する(メールアドレスやユーザーIDなど) |
db_index |
データベースのインデックス |
True にすると、そのカラムに DB インデックスを作成し、検索速度を向上させる |
primary_key |
主キーの指定 |
primary_key=True を指定したフィールドがテーブルの主キー(PK)になる |
default |
デフォルト値 | レコード登録時に値が指定されなかった場合の初期値(例: default=False) |
max_length |
最大文字数 | 文字列の最大文字数。CharField では指定が必須
|
choices |
選択肢の制限 | 登録・更新時の入力値をタプルや Enum で定義した選択肢のみに制限する(ステータス管理など) |
blank |
入力バリデーション設定 |
ModelSerializer でのバリデーション時に空白(空文字列)を許可するか(デフォルトは False) |
validators |
バリデータ関数の指定 | 正規表現チェックや数値範囲チェックなど、独自の検証ロジックをリスト形式で渡す |
on_delete |
関連先削除時の挙動 |
ForeignKey や OneToOneField で 指定必須(models.CASCADE や models.PROTECT など) |
💡 null と blank の決定的な違い(超頻出!)
-
null=True: データベース層 の設定。カラムにNULLを保存してよいかを指定。 -
blank=True: アプリケーション層(DRFシリアライザ・フォーム) の設定。API リクエストでその項目が空(空文字列や未送信)でもバリデーションエラーにしないかを指定。
9. クラスメタ(Meta クラス)によるモデル設定
モデルクラスの内部に class Meta: を定義することで、テーブル全体に関わる動作やメタ情報をカスタマイズできます。
class Message(models.Model):
title = models.CharField(max_length=100)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
db_table = 'custom_message' # テーブル名を明示的に指定
ordering = ['-created_at'] # 新しい順(降順)で取得
verbose_name = 'メッセージ'
verbose_name_plural = 'メッセージ' # 複数形も統一
class Meta で設定できる主要な項目一覧です。
| 設定項目 | 意味・役割 | 実務での使われ方 |
|---|---|---|
db_table |
実際の DB テーブル名を指定 | デフォルトの アプリ名_モデル名 以外の命名にしたい場合や、既存 DB 接続時 |
ordering |
デフォルトの並び順 | クエリ発行時の並び順。- をつけると降順(例: ['-created_at'] は最新順) |
verbose_name |
単数形の表示名 | 管理画面などで表示されるモデルの名前 |
verbose_name_plural |
複数形の表示名 | 英語では自動で末尾に「s」が付くため、日本語では verbose_name と同じ値を設定 |
indexes |
DB インデックスの設定 | 複合インデックス(複数カラムを組み合わせた高速検索)を貼る際に利用 |
constraints |
DB 制約の設定 | Check 制約や複合 Unique 制約など、より高度な DB レベルの制約を宣言 |
unique_together |
複数項目の一意制約 | 2つ以上のカラムの組み合わせで重複を禁止(※現在は constraints が推奨) |
permissions |
独自権限の追加 | アプリ固有のカスタムパーミッション(「閲覧のみ可能」など)を追加定義 |
default_permissions |
デフォルト権限の制御 | 自動作成される add/change/delete/view 権限を制御 |
abstract |
抽象基底モデルにする |
abstract = True にするとテーブルを作らず、共通フィールドを持つ親クラスとして継承可能に |
managed |
Django のテーブル管理有無 |
False にすると、Django がマイグレーション(テーブル作成・変更)を行わない(外部DB参照時) |
実務では、特に ordering(並び順) と verbose_name_plural(管理画面の日本語化) はほぼすべてのモデルで設定します。
10. (参考)モデルのフック(ライフサイクルメソッド)
Django のモデルには、データが「保存される瞬間」や「削除される瞬間」など、モデルのライフサイクルに合わせて自動で呼び出される フック(メソッド) が用意されています。
序盤のうちは、先ほどコード解説で登場した __str__()(表示用の文字列を返す) をしっかり押さえておけば開発に困ることはありません。発展的なカスタマイズとして、以下のようなメソッドが存在することを頭の片隅に置いておくと良いでしょう。
| メソッド | 呼ばれるタイミング | 主な用途の例 |
|---|---|---|
__str__(self) |
オブジェクトが文字列化されたとき | 必須級。管理画面やログでの表示文字(タイトルなど)を返す |
save(self, *args, **kwargs) |
instance.save() で保存されるとき |
保存直前の自動データ補完(スラグの自動生成など) |
delete(self, *args, **kwargs) |
instance.delete() で削除されるとき |
削除時に紐づく実ファイル(ストレージ画像など)のクリーンアップ |
💡 実務でのワンポイント:ロジックはシリアライザと住み分ける
DRF を使った API 開発では、リクエストデータのバリデーションや加工は シリアライザ(Serializer) で行うのが基本です。モデルの save() を過剰にカスタマイズすると処理の流れが見えにくくなるため、「管理画面からでもAPIからでも絶対に共通して行いたい最低限の処理」に留めるのが綺麗な設計のコツです。
まとめ
第四講では、DRF の全体アーキテクチャの把握から、Django ORM による本格的なデータ設計(モデル)の基礎までを一気に学びました!
-
REST API & DRF の全体像:
- クライアントとサーバー間の JSON 通信と HTTP メソッド(GET/POST/PUT/DELETE)。
- Django の強固な基盤の上に、DRF の武器(Serializer / ViewSet)が乗る構造。
-
1リクエストの処理シーケンス:
- ミドルウェアによる検問 ➔ URLルーティング ➔ ViewSet ➔ Serializer入力検証 ➔ ORM経由のDB保存 ➔ Serializer出力整形 ➔ レスポンス返却という 6 ステップの流れ。
-
オブジェクト指向と Django モデル(ORM)の関係:
- クラス(設計図)➔ テーブル、属性(データ)➔ カラム、インスタンス(実体)➔ レコードという美しい対応関係。
-
models.Modelを継承することで、SQLを書かずに強力なORM機能を自在に扱える。
-
モデルの書き方とフィールドクラス:
-
models.Model継承によるテーブル定義と、CharFieldやDateTimeFieldなど多彩なフィールド型。
-
-
フィールドオプション & クラスメタ:
-
null/blankやon_deleteなどのオプション制約。 -
class Metaによる並び順(ordering)やテーブル名の一括管理。 -
__str__()を定義して管理画面での視認性を大幅アップ。
-
次回予告
次回は 【第五講】マイグレーション完全攻略・Django管理画面とリレーション設計 に進みます!
今回定義した Python のモデルを、いよいよコマンド(makemigrations / migrate)を使って実際のデータベース(SQLite)へと安全に反映させます。
さらに、Django 組み込みの超強力な「管理画面(Admin)」を有効化し、ブラウザからデータをポチポチ追加・確認できる環境を整えていきましょう。お楽しみに!





