◀ 2.

DjangoのMVC・MVTとは|URL・View・Model・Templateの役割と実例

▶

この記事はDjango配下の設計・実装の入口です。Modelはデータ、Viewはリクエストに対する処理とresponse、Templateは表示を受け持ちます。URL設定はどのViewを呼ぶかをframeworkへ伝えます。MVT/MTVという文字順だけで処理順を覚えないでください。

URLの振分けからViewへ入り、必要ならModelとTemplateを使ってresponseを返す流れ

調べたい役割から子ページを選ぶ

次の子ページから対象を選択してください。一般MVCの他製品比較は後半に残していますが、Djangoの実装を調べる場合は先にこちらを使います。

名前の置換ではなく処理の担当を見る

Django 5.2公式FAQは、Viewを「どのデータを見せるか」、Templateを「どう見せるか」と整理し、Controllerに当たるものをURL設定に従ってViewへ振り分けるframework自体と説明します。「ViewとControllerが逆転」と一対一で暗記するとURLconfの仕事が抜けます。

すべてのViewがDBやTemplateを使うわけではありません。HttpResponseやJSONを直接返すことも、アクセスを拒否することもあります。責務分離は認証/認可・入力検証・transaction・例外処理を自動で満たす保証ではなく、必要な境界を設計して検査します。

1件表示の小さな実例

Python3.12.10/Django5.2.18で専用の空設定と一時メモリDBを使って確認する例です。既存projectにsettingsを上書きしたりDB作成を実行する手順ではありません。以下は別々の4ファイルです。新しい検証用projectで、このappをINSTALLED_APPSに登録し、DjangoTemplatesのAPP_DIRS=Trueを設定し、projectのURLconfからこのappのurlsをincludeする構成を前提にします。

catalog/models.py:教材用の公開商品名だけを持つ独自モデルです。標準auth.Userのname/emailを仮定せず、顧客/秘密情報を表示しません。

from django.db import models


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

catalog/views.py:存在しないpkは404、存在すれば辞書contextでTemplateへ渡します。公開カタログを読むだけの例で、個人別データの認可や書込みAPIは含みません。

from django.shortcuts import get_object_or_404, render

from .models import CatalogItem


def detail(request, pk):
    item = get_object_or_404(CatalogItem, pk=pk)
    return render(request, "catalog/detail.html", {"item": item})

catalog/urls.py:整数converterで一致した値をViewのpk引数へ渡します。ルートURLではなく、include先のprefixとの組合せで実URLが決まります。

from django.urls import path

from . import views

app_name = "catalog"
urlpatterns = [path("items/<int:pk>/", views.detail, name="detail")]

catalog/templates/catalog/detail.html:app名のdirectoryで名前を分けます。通常のautoescapeを保ち、safeで入力をそのままHTML化しません。

<!doctype html>
<html lang="ja">
<meta charset="utf-8">
<title>公開カタログ</title>
<h1>{{ item.name }}</h1>
</html>

この例のfixtureではproject側でpath("catalog/", include("catalog.urls"))を登録し、/catalog/items/1/が表示URLになります。reverseはcatalog:detailへpkを渡します。実projectのschema作成はその環境の承認済み手順に従い、CWiki本番でmigrateする指示ではありません。

表示されない時は境界を順に確認する

  1. URLが一致しない:project側のinclude prefix、末尾slash、converterと引数名を確認します。数値でないpathはこの例ではViewまで届きません。
  2. 404になる:route不一致と、Viewが対象行を見つけられない場合を分けます。pkは権限の証明ではなく、非公開データでは認可済みQuerySet等を別に用意します。
  3. TemplateDoesNotExist:template名、配置、APP_DIRS/DIRS、app登録を確認。base.htmlをextendsするならその存在とblock名も必要です。
  4. 表示が空:contextのキーと属性を確認します。通常のDjangoTemplateで未定義変数が空文字になる場合もあり、「エラーがないから正しい」と判断しません。
  5. モデル定義とDBが違う:Modelのコード、実schema、利用DBを区別します。動作確認のために実DBを初期化したり権限を広げたりしません。

fixtureではroute/reverse、正常表示、存在しない行、非整数path、template探索とescape等を確認します。実ブラウザ、実projectの認証/認可、他製品の比較コードの実行や本番運用まで検証した意味ではありません。

一次資料:Django 5.2 Tutorial 3、URL dispatcher。利用中の版と照合してください。

この記事の要点
  • MVC / MVT は責務の分け方の説明。3台構成や物理的な3層とは別
  • MVC: Model (データ) / View (表示) / Controller (制御)
  • MVT / MTV: Model / View / Template。Django公式FAQはURL振分けを含むframework自体をControllerに対応づける
  • 主要フレームワーク: Spring MVC / Laravel / Rails (MVC) / Django (MVT)
  • 責務分離でテスト容易・保守性向上・並行開発しやすくなる

 

MVC とは

MVC (Model-View-Controller) はアプリケーションの責務を分ける設計パターンです。Web専用ではなく、実装によって役割の境界も異なります。以下はサーバー側HTML生成の説明用の比較です。

層役割例
ModelデータとビジネスロジックDB 接続、エンティティ、サービスクラス
View表示(プレゼンテーション)HTML テンプレート、JSP、Blade、Twig
Controllerリクエスト処理と View 選択URL ハンドラ、@Controller クラス

MVC のフロー

  1. ユーザがブラウザでリクエスト送信
  2. Controller がリクエストを受信
  3. Controller が Model を呼び出してデータ取得
  4. Controller が View にデータを渡す
  5. View が HTML を生成してレスポンス

主要フレームワーク別の実装

Spring MVC (Java)

比較例の条件:Springの比較用断片です。別ファイルのimports/Repository/設定等を省略しています。示したUserにはemailがないため、次のテンプレートのemail参照をそのまま実行可能と扱いません。SpringのModel引数は画面へ渡す属性用で、JPA Entityそのものとは別です。

// Controller
@Controller
@RequestMapping("/users")
public class UserController {

    @Autowired
    private UserService userService;  // ← Model 層

    @GetMapping("/{id}")
    public String show(@PathVariable Long id, Model model) {
        User user = userService.findById(id);  // Model 呼び出し
        model.addAttribute("user", user);
        return "users/show";  // ← View 名(Thymeleaf テンプレート)
    }
}

// Model (Entity + Service)
@Entity
public class User {
    @Id private Long id;
    private String name;
}

@Service
public class UserService {
    @Autowired private UserRepository userRepository;

    public User findById(Long id) {
        return userRepository.findById(id).orElseThrow();
    }
}

比較例の条件:前のUserのnameのみでemailは未定義です。実モデルの公開可能な属性に合わせてテンプレートを修正します。

<!-- View: resources/templates/users/show.html (Thymeleaf) -->
<h1 th:text="${user.name}">名前</h1>
<p th:text="${user.email}">メール</p>

Laravel (PHP)

比較例の条件:Laravelの別ファイルをまとめた比較例です。namespace/imports/routes/DB構成が必要で、この断片だけでは動きません。fillableは認可の代わりではありません。

// Controller: app/Http/Controllers/UserController.php
class UserController extends Controller {
    public function show($id) {
        $user = User::findOrFail($id);  // Model (Eloquent)
        return view("users.show", compact("user"));  // View 呼び出し
    }
}

// Model: app/Models/User.php
class User extends Model {
    protected $fillable = ["name", "email"];
}

比較例の条件:Laravel Blade専用です。Djangoテンプレートと構文を混ぜず、ユーザー情報を見せてよい範囲を別に認可します。

<!-- View: resources/views/users/show.blade.php -->
<h1>{{ $user->name }}</h1>
<p>{{ $user->email }}</p>

Rails (Ruby)

比較例の条件:Railsのcontroller/modelは別ファイルです。DB schema/routes等とname/email属性を仮定した比較例で、実行検証していません。

# Controller: app/controllers/users_controller.rb
class UsersController < ApplicationController
  def show
    @user = User.find(params[:id])  # Model
    # 自動的に app/views/users/show.html.erb をレンダリング
  end
end

# Model: app/models/user.rb
class User < ApplicationRecord
end

比較例の条件:Rails ERB専用です。実際のデータ属性と認可を確認します。

<!-- View: app/views/users/show.html.erb -->
<h1><%= @user.name %></h1>
<p><%= @user.email %></p>

MVT とは (Django)

単にViewとControllerの名称が逆転するわけではありません。Django公式FAQではViewは何を提示するか、Templateはどう表示するかを担い、Controllerに当たるのはURL設定から適切なViewへ振り分けるframework自体と説明しています。

標準 MVCDjango (MVT)説明
ModelModel同じ
View (HTML)TemplateHTML テンプレート
Controllerとの比較URLconfを使うframeworkの振分けとView厳密な一対一対応ではない。Viewは提示データとresponseを決める

Django (Python) の例

比較例の条件:Django旧比較例です。urls.py/views.py/models.pyは別ファイル、Userは独自モデルで標準auth.Userではありません。Controller相当というコメントは簡略化で、公式FAQのframework側の振分けも含めて読みます。

# urls.py
from django.urls import path
from . import views

urlpatterns = [
    path("users/<int:id>/", views.user_detail, name="user_detail"),
]

# views.py (役割は MVC の Controller)
from django.shortcuts import render, get_object_or_404
from .models import User

def user_detail(request, id):
    user = get_object_or_404(User, pk=id)  # Model 呼び出し
    return render(request, "users/detail.html", {"user": user})  # Template

# models.py (Model)
from django.db import models

class User(models.Model):
    name = models.CharField(max_length=100)
    email = models.EmailField()

比較例の条件:前の旧例はbase.htmlの提供を省略しています。TemplateDoesNotExistやblock名の不一致を確認し、safeによるHTML無条件出力で回避しません。

<!-- templates/users/detail.html (Template) -->
{% extends "base.html" %}
{% block content %}
    <h1>{{ user.name }}</h1>
    <p>{{ user.email }}</p>
{% endblock %}

MVC のメリット

  • 責務分離: 依存を整理すると対象を分けてテストしやすい。ORM/DB等が必要な統合試験もある
  • 並行開発: フロント (View) とバック (Controller/Model) を別チームで開発可能
  • 保守性: contextのキー/型・URL等の契約を守れば表示変更の影響を限定しやすい。無影響の保証ではない
  • 再利用: 同じ Model を複数 View で使い回せる
  • テスト容易: Model / Controller を独立して単体テスト可能

MVC の限界とモダンアーキテクチャ

純粋 MVC では Controller がビジネスロジックを抱え込む「Fat Controller」問題が起きがち。モダンな対策:

  • Service 層追加: 必要な場合にController/Viewから業務処理を分ける。常に4層へ固定する規則ではない
  • Repository 層: DB アクセスを抽象化(Model から分離)
  • DTO: Controller ↔ View 間のデータ転送オブジェクト
  • Form Request / DTO 検証: バリデーション層の分離

モダンな層構成例 (Spring Boot)

  • Controller: HTTP 受付、リクエスト → DTO 変換
  • Service: ビジネスロジック、トランザクション境界
  • Repository: DB アクセス (JPA / MyBatis)
  • Entity: DB マッピング
  • DTO: API レスポンス / リクエスト型

SPA + API での MVC

React/Vue + REST API の構成では、JSON APIの表示をフロント側が担う構成もある。SSR/HTMLとの併用等もあり、完全移動が必須ではない:

  • サーバ側: Model + Controller (Service 含む)。REST API で JSON 返却
  • クライアント側: View (React/Vue コンポーネント) + 状態管理

関連記事

子ページ
  1. ビュー(View)
  2. テンプレート(Template)
  3. モデル(Model)
  4. ルーティングの作成
  5. viewからtemplateへの遷移方法
同階層のページ
  1. 環境構築とプロジェクト/アプリの作成
  2. MVC(MVT)のそれぞれの使い方と説明
  3. データベースへの接続と操作
  4. Django Administration
  5. git管理
  6. エラー一覧
  7. バージョンの確認方法
  8. ログ出力方法
  9. SQLのログ出力方法
  10. ログのローテート設定
  11. settings.pyの定数にアクセスする方法
  12. 本番環境へのインストールとアプリのデプロイ(apache編)
  13. 本番環境へのインストールとアプリのデプロイ(nginx編)
  14. djangoアプリの本番の開始URLを変更する
  15. 静的(static)ファイルの置き場所と読み込み(画像、css、js )
  16. CSRFトークンをAjaxで使用する方法
  17. ajaxの使用例(POST編)
  18. ファイルのアップロードとファイルの名前
  19. クイックスタート/チュートリアル
  20. ログイン機能
  21. テンプレート側のログイン判定
  22. ビュー側のログイン判定
  23. 管理者ユーザーの作成/判定と管理画面
  24. モデルのjson化とレスポンス
  25. runserverでポートを指定する方法
  26. cronによるバッチ実行
  27. テンプレートで利用する共通のcontextを定義する方法
  28. プログラムが本番サーバーで反映されない場合の対処法
  29. APIの作成
  30. cron用コマンド・ファイルの作成

最近更新/作成されたページ