Djangoは「どこに何を書くか」が決まっている ― MTVと最小構成
articles/views.py を書いてブラウザを開いたのに、Djangoの初期ページのままだった。素朴なPHP構成なら article.php を置いた場所をそのままURLにできますが、Djangoでは何も起きません。ファイルを置いた場所とURLは、まったく関係がないからです。
この挙動は戸惑いやすい一方、裏を返せば「どこに何を書くか」が最初から決まっている、ということでもあります。本記事は「Djangoフレームワーク入門と実践」の第1回として、その決まりごと(MTV)と、プロジェクトが動き出すまでの最小構成を扱います。
シリーズの前半(第1〜3回)では、記事を投稿・表示する小さなCMSを題材にDjangoの基礎を一通り通します。後半(第4回以降)は、姉妹シリーズ「Springフレームワーク入門と実践」と題材を注文受付へ揃えたうえで、同じ処理をSpring Bootで書いた場合と比較していきます。
検証環境はPython 3.11、Django 5.2.17、SQLiteです。2026年8月時点の最新機能リリースはDjango 6.1ですが、このシリーズは長期サポート版(LTS)である5.2系に固定しました。5.2系の公式な延長サポートは2028年4月までで、記事中のリンクも5.2版へ揃えています。
この記事でわかること
- Djangoが「フルスタック」と呼ばれる理由と、最初から入っているもの
- MTV(Model・Template・View)が、どのファイルに対応するか
-
startprojectとstartappの関係 - エラーが出ないまま失敗する2つのつまずき所と、その直し方
選ばなくていい代わりに、Django流に合わせる
DjangoはPythonのフルスタックWebフレームワークです。「フルスタック」が具体的に何を指すかというと、次のものが最初から同梱されているということです。
| 機能 | Djangoでの実体 | 素朴なPHP構成なら |
|---|---|---|
| DB操作 | ORM(SQLをほぼ書かない) | 自分でSQLを書く/ライブラリを選ぶ |
| URLルーティング |
urls.py の対応表 |
ファイルの配置がそのままURL |
| 管理画面 | django.contrib.admin |
自作するか、別途用意する |
| 認証・セッション | django.contrib.auth |
自作するか、ライブラリを選ぶ |
| 開発用サーバ | manage.py runserver |
Apacheやlighttpdを立てる |
選ぶ手間がない代わりに、Django流のやり方に合わせる必要があります。この記事で扱うのは、その骨格です。
MTV ― 1つのリクエストが通る道
Djangoの構造はMTV(Model・Template・View)と呼ばれます。名前だけ聞くとMVCの言い換えのようですが、対応するファイルで捉えたほうが早いです。
図はファイル4つを並べただけですが、通る順番と、MTVのどれがどれに当たるかを対応させています。
このCMSの一覧表示を順に追うと、urls.py がURLと処理の対応を決め、views.py が呼ばれ、models.py 経由でデータを取り、テンプレートがHTMLにする、という流れです。すべてのリクエストが必ずこの4段を通るわけではありません。DBを使わず直接レスポンスを返すViewも、Templateを使わずJSONを返すViewも書けます。
- Model — DBのテーブルをPythonのクラスで書きます。検索や保存もこのクラス経由です
- View — リクエストを受け取り、Modelからデータを集めてTemplateへ渡す処理です。MVCでいうControllerに近い位置づけです
-
Template — 返すHTMLです。
{{ 変数名 }}や{% for %}という独自の記法で値を埋め込みます
MVCのViewとMTVのViewは、別のものを指します
MTVで表示を担当するのはTemplateで、Viewは処理側です。MVCの語彙をそのまま当てると、ここを混同しやすくなります。Djangoの公式FAQも、MVCとの対応を説明しています。
設定を持つのがプロジェクト、機能を持つのがアプリ
まず作業用の仮想環境を作ってDjangoを入れます。Django 5.2が公式対応するPython 3.10〜3.14のうち、この記事ではPython 3.11を使います。
python -m venv .venv
source .venv/bin/activate # Windowsは .venv\Scripts\activate
pip install "Django==5.2.*"
以降のコマンドは、この仮想環境を有効にした状態で実行します。
django-admin startproject mysite
cd mysite
python manage.py startapp articles
1つ目が django-admin、2つ目が python manage.py になっているのは、プロジェクトを作る前と後の違いです。manage.py はプロジェクトと一緒に生成されるので、それ以降はこちらを使います。
startproject で作られる mysite/ がサイト全体の設定を持ちます。settings.py(設定)と urls.py(ルーティングの入口)が中心です。startapp で作られる articles/ が機能のまとまりです。記事機能、ユーザー機能、通知機能……とアプリを分けていく想定になっています。
この時点のファイル構成はこうなります。
mysite/
├── manage.py ← コマンドの入口
├── mysite/ ← プロジェクト(サイト全体の設定)
│ ├── settings.py
│ ├── urls.py ← ルーティングの入口
│ └── wsgi.py ← 本番サーバとつなぐ口
└── articles/ ← アプリ(機能のまとまり)
├── models.py
├── views.py
├── admin.py
└── migrations/ ← DBへの変更履歴が入る(第2回)
templates/ はこの時点では作られません。自分で作ります(第3回)。
ロケットが出れば、まだ何も定義していなくて正常
python manage.py runserver
http://127.0.0.1:8000/ を開いてロケットの絵が出れば、プロジェクトは動いています。まだURLを1つも定義していないので、この時点ではこの初期ページが出るのが正常です。あとで出てくるつまずき所②で、この画面がもう一度、別の意味で出てきます。
Watching for file changes with StatReloader
Performing system checks...
System check identified no issues (0 silenced).
Django version 5.2.17, using settings 'mysite.settings'
Starting development server at http://127.0.0.1:8000/
runserver は開発用です。起動時のログにも「本番では使うな」という警告が出ます。本番ではWSGI/ASGIサーバやリバースプロキシを含めた配備構成が別途必要です。このシリーズでは扱いません。
つまずき所 ① ― アプリを登録しないと、何も起きない
ここからが本題です。startapp articles を実行しても、Djangoはまだ articles アプリの存在を知りません。
この状態でモデルを書いて、DBへ反映するためのファイル(マイグレーション。第2回で扱います)を作ろうとすると、こうなります。
$ python manage.py makemigrations
No changes detected
$ echo $?
0
エラーではありません。終了コードも0です。ただ「変更はありません」と言われるだけで、何も起きません。モデル定義だけを見直しても直らず、確認すべき場所はアプリの登録です。
原因は、アプリが登録されていないことです。settings.py の INSTALLED_APPS へ追加します。
# settings.py
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"articles",
]
上の6つは、最初から入っている機能(管理画面・認証・セッションなど)です。Djangoにとっては自前の機能も自分で作るアプリも同じ「アプリ」で、この一覧に並んでいるものだけが有効になります。登録していないものは、黙って無視されます。
認識されているアプリは python manage.py showmigrations で確認できます。articles が並んでいなければ、モデル側をいくら見ても何も出てきません。
つまずき所 ② ― urls.py をつながないと、ロケットのまま
もう1つは、ルーティングです。アプリ側に urls.py を作っても、それだけでは有効になりません。プロジェクト側の urls.py から include() して、初めてURLと結びつきます。
# mysite/urls.py
from django.contrib import admin
from django.urls import include, path
urlpatterns = [
path("admin/", admin.site.urls),
path("", include("articles.urls")),
]
これを忘れるとどうなるか。/admin/ は問題なく開けるのに、/ はさっきのロケットの初期ページのままになります。404すら出ません。
urls.py の中身が admin/ だけのとき、Djangoは / に対してこの初期ページを返す仕様だからです(手元で確認すると 200 でした)。DEBUG = False なら404になりますし、urls.py に他のパターンが1つでもあれば404になります。
つまり、同じ画面が2つの状態で現れます。「まだ何も定義していないので正常」と「アプリをつなぎ忘れている」が、見た目だけでは区別できません。views.py を疑う前に、プロジェクト側の urlpatterns を確認するのが近道です。
日本語で使うなら、最初にやっておく設定
# settings.py
LANGUAGE_CODE = "ja"
TIME_ZONE = "Asia/Tokyo"
USE_TZ = True
LANGUAGE_CODE を ja にすると、管理画面の表示が日本語になります。TIME_ZONE は表示に使うタイムゾーンです。USE_TZ = True(既定値)のときDjangoはDBにはUTCで保存し、表示するときに TIME_ZONE へ変換します。第3回で管理画面を開くと、DBの値と画面の表示が9時間ずれているのを実際に見ることになります。
実務ではこうなる
アプリは、最初から細かく分けないほうがいいです。「機能のまとまり」と言われると10個に割りたくなりますが、アプリをまたぐモデル参照が増えると逆に追えなくなります。今回のCMSも articles 1つで足りました。境界がはっきりしてから分けるほうが動きやすいです。
一方、settings.py の分割と SECRET_KEY の追い出しは、gitへ入れる前に必ず来ます。開発と本番でDB接続先も DEBUG も違うものを1ファイルに書くのは無理がありますし、startproject が生成した鍵はそのまま平文で残っています。どちらも第7回で、Springの外部設定・Profileと並べて扱います。
確認した範囲
Django 5.2.17、Python 3.11。startproject/startapp 直後のファイル構成、INSTALLED_APPS 未登録の状態で makemigrations が No changes detected(終了コード0)で終わること、LANGUAGE_CODE/TIME_ZONE の変更が管理画面の表示に反映されることを、それぞれ実行して確かめました。
つまずき所②は、urls.py が admin/ だけの状態で / を開き、404ではなくロケットページが 200 で返ることを確認しています。urls.py にもう1つパターンを足すと404に変わることも併せて確認しました。
公式リファレンス
この回で扱った機能
- Django 5.2: はじめての Django アプリ作成
- Django 5.2: Applications(
INSTALLED_APPS) - Django 5.2: URL dispatcher
- Django 5.2: Time zones
バージョンを選ぶときの根拠
参考文献・参照資料
本記事は公式ドキュメントとサンプルのローカル実行結果を根拠にしています。書籍は参照していません。図は新規に作成したもので、公式資料の図版を引き写したものではありません。
次の第2回では、モデルを書いて実際にテーブルを作ります。makemigrations が生成するファイルの中身と、そこから発行される CREATE TABLE を実際に読み、「SQLを書かない」とはどういう意味かを確認します。
