- Python
Djangoとは・使い方入門|6.1でアプリを1本作る全コマンドと出力
結論
Djangoは、PythonでWebアプリを作るためのフレームワークで、データベース操作(ORM)・管理画面・ログイン・フォーム・セキュリティ対策が最初から入っています。使い方は「startproject → startapp → モデル → migrate → 管理画面 → ビュー・URL → テンプレート → フォーム」の順に進めると、1本のアプリが完成します。
この記事でわかること
- Django 6.1.2 でタスク管理アプリを作り切る全コマンドと出力
- migrate が実際に発行した SQL と、管理画面・フォームの画面
- check --deploy の原文と、よくあるエラー12種の直し方
Django では、ブラウザからのリクエストを URL の設定(urls.py)が受け取り、ビュー(views.py)がモデル(models.py)を通してデータを読み書きし、テンプレート(HTML)で画面を組み立てて返します。この役割分担を MTV(Model・Template・View)と呼びます。
flowchart TD
B[ブラウザ] -->|リクエスト| U[urls.py<br>URL を振り分け]
U --> V[View<br>views.py]
V <-->|ORM| M[Model<br>models.py]
M <--> DB[(データベース)]
V -->|データを渡す| T[Template<br>HTML]
T -->|レスポンス| B
よく調べられることの答えを先にまとめます。
| 知りたいこと | 答え | 詳しい節 |
|---|---|---|
| Djangoとは | Python のフルスタックな Web フレームワーク。ORM・管理画面・認証が標準 | Djangoとは |
| Django 使い方の流れ | startproject → startapp → モデル → migrate → admin → ビュー → テンプレート → フォーム | プロジェクトとアプリを作る |
| Django 入門に要る Python | Django 6.1 は Python 3.12 以上。3.11 だと 5.2 LTS が入る | Djangoの版とインストール |
| インストール方法 | venv を作って pip install django |
Djangoの版とインストール |
| MTV と MVC の違い | 名前の付け方の違い。Controller にあたるのはフレームワーク自身 | Djangoとは |
| データベースの作り方 | models.py を書いて makemigrations → migrate | モデルとマイグレーション |
| 管理画面の出し方 | createsuperuser → admin.py に登録 → /admin/ | Django管理画面 |
| 本番に出す前の確認 | manage.py check --deploy。runserver は本番に使わない |
本番前のcheck --deploy |
| Flask・FastAPI との違い | 機能が揃った Django/小さく始める Flask/API 中心の FastAPI | Flask・FastAPIとの違い |
確認環境:Ubuntu 24.04.5 LTS の上で、作業用のフォルダーに Python 3.13.18 の venv を作り、pip で Django 6.1.2(asgiref 3.13.0・sqlparse 0.6.0)を入れました。django-admin startproject から、モデル・マイグレーション(SQLite)・管理画面・関数ビューとクラスベースビュー・テンプレート・ModelForm・テストまでを1本のタスク管理アプリとして作り、開発サーバーを 127.0.0.1 の自分のポートで起動して、curl 8.5.0 と Chromium 153.0.8010.12(Playwright・日本語の設定)で画面と応答を確かめました。本番用の起動は Gunicorn 26.2.0 を 127.0.0.1 で動かして比べています。Python 3.11 で入る版は pip の --dry-run で確かめました。版の対応・サポート期限・デプロイの選択肢・MTV の説明は Django の公式ドキュメント(6.1)と djangoproject.com で確かめ、Windows・Mac でのインストールの細かな手順とクラウドへのデプロイは動かしていません。図は mermaid で描画を確かめています(確認日 2026年10月29日)。
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Djangoとは・使い方入門|6.1でアプリを1本作る全コマンドと出力」
Djangoとは(できること・特徴・MTV)
この節では、Django が何をしてくれるフレームワークで、どんな仕組みで動くのかが分かります。
Django(ジャンゴ)は、Python で書かれた無料・オープンソースの Web フレームワークです。公式サイトは「締め切りのある完璧主義者のための Web フレームワーク」と紹介し、速く作れること、セキュリティを重視していること、大きなサイトにも広げられることを特徴に挙げています。ライセンスは BSD ライセンス(GitHub の LICENSE)で、名前はジャズ・ギタリストのジャンゴ・ラインハルトから取られています(公式 FAQ。読みは「JANG-oh」で D は発音しない)。
Web アプリに要る部品の多くが、最初から入っています。この記事のアプリで実際に使ったものに印を付けました。
| 機能 | 何をしてくれるか | この記事で使った |
|---|---|---|
| ORM | Python のクラスでテーブルを定義し、SQL を書かずに読み書きする | 使った(SQL も表示) |
| マイグレーション | モデルの変更から、テーブルを作り変える手順を自動で作る | 使った(2回) |
| 管理画面(admin) | モデルを登録するだけで一覧・検索・編集の画面ができる | 使った |
| 認証(auth) | ユーザー・グループ・権限・ログイン | 管理者ユーザーで使った |
| フォーム | 入力欄の HTML・入力チェック・エラー表示 | ModelForm で使った |
| テンプレート | HTML の継承・自動エスケープ | 使った |
| セキュリティ | CSRF・XSS・クリックジャッキング対策、Host ヘッダーの検査 | 403・400 を実際に出した |
| 国際化 | 画面の言語・タイムゾーン | LANGUAGE_CODE="ja" で管理画面が日本語に |
| テスト | テスト用 DB・テストクライアント | 4件のテストを実行 |
MTV と MVC の関係
Django の公式 FAQ は、Django は MVC に見えるが、Controller を「ビュー」、View を「テンプレート」と呼んでいる、と書いています。Django の「ビュー」は「どのデータを見せるか」を決める Python の関数で、「どう見せるか」はテンプレートに任せます。Controller にあたるのは、URL の設定に従ってリクエストを正しいビューへ送るフレームワーク自身で、略すなら MTV(Model・Template・View)だ、としています。
| MVC の言い方 | Django の言い方 | ファイル |
|---|---|---|
| Model | Model | models.py |
| View(見た目) | Template | templates/…/*.html |
| Controller | View + フレームワーク(URL の振り分け) | views.py・urls.py |
公式 FAQ では、Disqus・Instagram・Pinterest・Mozilla が長年 Django を使っている例として挙がっています。API を作る拡張として Django REST framework や Django Ninja、WebSocket を扱う Django Channels などの外部パッケージもあります。
Djangoの版とインストール(venv・Pythonの対応)
この節では、2026年10月29日時点の Django の版と Python の対応、venv に入れる手順が分かります。
2026年10月29日時点の最新版は Django 6.1.2 です。長期サポート(LTS)は 5.2 で、次の LTS の 6.2 は2027年4月の予定です。2028年からは版の番号が年(Django 2028)になります(Django 6.1 のリリースノート)。
| 版 | 最新 | 対応する Python | セキュリティ修正の期限 |
|---|---|---|---|
| 6.1 | 6.1.2 | 3.12・3.13・3.14(3.15 は 6.1.3 から) | 2027年12月 |
| 6.0 | 6.0.9 | 3.12・3.13・3.14 | 2027年4月 |
| 5.2 LTS | 5.2.18 | 3.10〜3.14 | 2028年4月 |
| 6.2 LTS(予定) | 2027年4月公開予定 | 公式で公開待ち | — |
新しく作るなら最新の 6.1、長く同じ版で動かしたいなら 5.2 LTS が目安です。Python 3.11 の環境で pip install django をすると、6.x ではなく 5.2 系が入ります。手元の Python 3.11 で確かめた結果です。
$ python3.11 -m venv v311
$ v311/bin/pip install --dry-run django
Would install Django-5.2.18 asgiref-3.13.0 sqlparse-0.6.0
venv を作って Django を入れる
プロジェクトごとに venv(仮想環境)を作り、その中に入れます。手元では次のとおりでした。
$ python3 -m venv venv
$ source venv/bin/activate # Windows は venv\Scripts\activate
(venv) $ python -m pip install django
Installing collected packages: sqlparse, asgiref, django
Successfully installed asgiref-3.13.0 django-6.1.2 sqlparse-0.6.0
(venv) $ python -m django --version
6.1.2
依存は asgiref と sqlparse の2つだけでした。Windows の有効化のコマンドは Python 公式ドキュメント(venv)にあるもので、手元の Linux では動かしていません。uv を使う場合の venv の作り方は、関連記事「uv venvとuvの使い方|仮想環境の作成・有効化とpipとの速さ比較」で扱っています。
プロジェクトとアプリを作る(startproject・runserver・startapp)
この節では、Django の最初の3つのコマンドと、作られるファイルの役割が分かります。
Django では「プロジェクト(サイト全体の設定)」の中に「アプリ(機能のまとまり)」を作ります。この記事では、プロジェクトを config、アプリを tasks にしました。
プロジェクトを作る
django-admin startproject config .。末尾の . で、今のフォルダーに manage.py を置く(0.87秒で終わった)。
開発サーバーを起動する
python manage.py runserver 127.0.0.1:45734。ポートを省くと 8000 番。
最初のテーブルを作る
python manage.py migrate。認証やセッションの18個のマイグレーションが入る。
アプリを作って登録する
python manage.py startapp tasks のあと、settings.py の INSTALLED_APPS に "tasks" を足す。
startproject で作られたファイルです。
manage.py
runserver・migrate・startapp などを実行する入口。このプロジェクトの設定を読み込んで動く。
いちばんよく使う
config/settings.py
INSTALLED_APPS・DATABASES・DEBUG・ALLOWED_HOSTS・言語など、全体の設定。
SECRET_KEY も入っている
config/urls.py
URL とビューの対応表の入口。各アプリの urls.py を include でつなぐ。
admin/ だけが最初から入る
wsgi.py・asgi.py
本番の Web サーバー(Gunicorn・Uvicorn など)から Django を呼ぶ入口。
開発中は触らない
migrate の前に runserver を起動したときの出力です。「18 unapplied migration(s)」は、まだ migrate していないという案内で、エラーではありません。
$ python manage.py runserver 127.0.0.1:45734
Watching for file changes with StatReloader
Performing system checks...
System check identified no issues (0 silenced).
You have 18 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.
Run 'python manage.py migrate' to apply them.
October 29, 2026 - 11:23:08
Django version 6.1.2, using settings 'config.settings'
Starting WSGI development server at http://127.0.0.1:45734/
Quit the server with CONTROL-C.
WARNING: This is a development server. Do not use it in a production setting. Use a production WSGI or ASGI server instead.
[29/Oct/2026 11:23:10] "GET / HTTP/1.1" 200 12149
ブラウザで開くと「The install worked successfully! Congratulations!」の初期画面(200)でした。ファイルを保存すると StatReloader が変更を見つけて自動で再起動します。runserver は親と子の2つのプロセスで動いていたので、止めるときは両方の PID を確かめて止めました。
settings.py で最初に変えた2行
INSTALLED_APPS = [
"django.contrib.admin",
# …(最初から入っている5つ)
"django.contrib.staticfiles",
"tasks", # 作ったアプリを足す
]
LANGUAGE_CODE = "ja" # 管理画面・エラー文が日本語になる
TIME_ZONE = "Asia/Tokyo"
Django 6.1 の startproject が作る settings.py には、新しい MAILERS の設定(開発用の console バックエンド)が入っていました。6.0 から DEFAULT_AUTO_FIELD の既定が BigAutoField になったため、この行は作られなくなっています(6.0 のリリースノート)。
モデルとマイグレーション(makemigrations・sqlmigrate・migrate)
この節では、モデルの書き方と、Django がテーブルを作るときに実際に流した SQLが分かります。
モデルは「テーブルの設計図」を Python のクラスで書いたものです。タスクのタイトル・メモ・期限・完了・作成日時を持たせました(優先度とその下の verbose_name は、あとから足した完成形です)。
from django.db import models
class Task(models.Model):
title = models.CharField("タイトル", max_length=100)
memo = models.TextField("メモ", blank=True)
due = models.DateField("期限", null=True, blank=True)
done = models.BooleanField("完了", default=False)
priority = models.PositiveSmallIntegerField("優先度", default=2, choices=[(1, "高"), (2, "中"), (3, "低")])
created_at = models.DateTimeField("作成日時", auto_now_add=True)
class Meta:
ordering = ["done", "due", "-created_at"]
verbose_name = "タスク"
verbose_name_plural = "タスク"
def __str__(self):
return self.title
モデルから「マイグレーション(テーブルの変更手順)」を作り、その SQL を見てから反映します。
$ python manage.py makemigrations
Migrations for 'tasks':
tasks/migrations/0001_initial.py
+ Create model Task
$ python manage.py sqlmigrate tasks 0001
BEGIN;
--
-- Create model Task
--
CREATE TABLE "tasks_task" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "title" varchar(100) NOT NULL, "memo" text NOT NULL, "due" date NULL, "done" bool NOT NULL, "created_at" datetime NOT NULL);
COMMIT;
$ python manage.py showmigrations tasks
tasks
[ ] 0001_initial
$ python manage.py migrate tasks
Running migrations:
Applying tasks.0001_initial... OK
- テーブル名は「アプリ名_モデル名」の小文字(
tasks_task)になり、主キーidは自動で付きました。 blank=Trueだけの memo はNOT NULLのままで、空文字で保存されます。null=Trueを付けた due だけがNULLを許しました。- showmigrations の
[ ]は未適用、適用後は[X]になります。
列を足すと、SQLite ではテーブルを作り直す
あとから優先度(priority)を足し、migrate を忘れたまま一覧を開くと、500 エラーになりました。
django.db.utils.OperationalError: no such column: tasks_task.priority
makemigrations と migrate で直ります。SQLite では、新しいテーブルを作ってデータを移し、名前を付け替える SQL が流れていました。
$ python manage.py makemigrations
Migrations for 'tasks':
tasks/migrations/0002_alter_task_options_task_priority.py
~ Change Meta options on task
+ Add field priority to task
$ python manage.py sqlmigrate tasks 0002
CREATE TABLE "new__tasks_task" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "priority" smallint unsigned NOT NULL CHECK ("priority" >= 0), "title" varchar(100) NOT NULL, …);
INSERT INTO "new__tasks_task" (…, "priority") SELECT …, 2 FROM "tasks_task";
DROP TABLE "tasks_task";
ALTER TABLE "new__tasks_task" RENAME TO "tasks_task";
既存の行には既定値の 2 が入りました。データが多いテーブルでは時間がかかるので、本番では sqlmigrate で中身を見てから流します。SQLite そのものの操作は、関連記事「SQLiteの使い方|コマンド早見表とPython連携を実行例で解説」で扱っています。
ORMでデータを操作する(shellと発行されたSQL)
この節では、Python のコードでデータを登録・検索する方法と、そのとき実際に流れた SQLが分かります。
python manage.py shell を開くと、モデルが自動で読み込まれた状態で試せます。Django 5.2 からの機能で、手元では13個の名前が自動で読み込まれました(-v 2 で一覧が出ます)。
>>> Task.objects.create(title="牛乳を買う", due=datetime.date(2026, 10, 30))
>>> Task.objects.create(title="記事を書く", memo="Django入門", due=datetime.date(2026, 10, 31))
>>> Task.objects.create(title="部屋の掃除", done=True)
>>> Task.objects.all()
<QuerySet [<Task: 牛乳を買う>, <Task: 記事を書く>, <Task: 部屋の掃除>]>
>>> Task.objects.filter(done=False).count()
2
>>> Task.objects.get(pk=1)
<Task: 牛乳を買う>
このとき流れた SQL です(connection.queries で表示)。Meta.ordering が ORDER BY に、all() の表示用に LIMIT 21 が付いていました。
INSERT INTO "tasks_task" ("title", "memo", "due", "done", "created_at") VALUES ('部屋の掃除', '', NULL, 1, '2026-10-29 11:23:41.372208') RETURNING "tasks_task"."id"
SELECT … FROM "tasks_task" ORDER BY "tasks_task"."done" ASC, "tasks_task"."due" ASC, "tasks_task"."created_at" DESC LIMIT 21
SELECT COUNT(*) AS "__count" FROM "tasks_task" WHERE "tasks_task"."done" = 0
SELECT … FROM "tasks_task" WHERE "tasks_task"."id" = 1 LIMIT 21
| やりたいこと | ORM の書き方 | SQL のイメージ |
|---|---|---|
| 全件 | Task.objects.all() |
SELECT … ORDER BY … |
| 条件で絞る | filter(done=False) |
WHERE done = 0 |
| 部分一致 | filter(title__contains="記事") |
LIKE %記事% |
| 1件だけ | get(pk=1)(無ければ DoesNotExist) |
WHERE id = 1 |
| 件数 | count() |
COUNT(*) |
| 登録 | create(…) か save() |
INSERT |
| 更新・削除 | save(update_fields=[…])・delete() |
UPDATE・DELETE |
N+1 を Django 6.1 の fetch_mode で減らす
関連先(外部キー)をループの中で読むと、1行ごとに SQL が流れます(N+1)。標準の Permission(28行)と ContentType で、クエリの回数を数えました。
| 書き方 | 行数 | クエリの回数 |
|---|---|---|
| 既定(FETCH_ONE) | 28 | 29 |
select_related("content_type") |
28 | 1 |
fetch_mode(models.FETCH_PEERS)(6.1 から) |
28 | 2 |
fetch_mode(models.FETCH_RAISE) |
— | FieldFetchBlocked で止まる |
FETCH_RAISE は「Fetching of Permission.content_type blocked.」で例外になり、開発中に N+1 を見つけるのに使えます。N+1 そのものの原因と見つけ方は、関連記事「N+1問題とは?原因・見つけ方・解決方法を実測比較【ORM別】」で扱っています。
Django管理画面(admin)の使い方
この節では、管理者ユーザーを作り、モデルを管理画面に出して使うまでが分かります。
admin.py にモデルを登録するだけで、一覧・検索・絞り込み・追加・編集・削除の画面ができます。
from django.contrib import admin
from .models import Task
@admin.register(Task)
class TaskAdmin(admin.ModelAdmin):
list_display = ["title", "due", "done", "created_at"]
list_filter = ["done"]
search_fields = ["title", "memo"]
$ python manage.py createsuperuser
(ユーザー名・メール・パスワードを対話で入れる。手元では環境変数 DJANGO_SUPERUSER_PASSWORD と --noinput で作成)
Superuser created successfully.
Chromium で /admin/ を開いた結果です。
- ログインしていないと
/admin/login/?next=/admin/へ移り、「Django 管理サイト」「ユーザー名」「パスワード」が日本語で出ました(LANGUAGE_CODE="ja" の効果)。 - 一覧では list_display の4列、右に「完了 で絞り込む(全て・はい・いいえ)」、上に検索欄が並び、完了は緑と赤のアイコンで表示されました。
- パスワードを間違えると、次の文言が出ました。
スタッフアカウントの正しいユーザー名とパスワードを入力してください。どちらのフィールドも大文字と小文字は区別されます。
最初、管理画面の見出しが英語の「Tasks」のままでした。モデルの Meta に verbose_name = "タスク" を足すと日本語になります。
ビューとURL(関数ビュー・クラスベースビュー)
この節では、リクエストを受けて画面を返すビューの書き方と、URL のつなぎ方が分かります。
ビューは「リクエストを受け取り、レスポンスを返す関数」です。一覧・登録・詳細・完了の切り替えの4つを書きました。
from django.contrib import messages
from django.shortcuts import get_object_or_404, redirect, render
from django.views.decorators.http import require_POST
from .forms import TaskForm
from .models import Task
def task_list(request):
tasks = Task.objects.all()
return render(request, "tasks/task_list.html", {"tasks": tasks})
def task_create(request):
if request.method == "POST":
form = TaskForm(request.POST)
if form.is_valid():
task = form.save()
messages.success(request, f"「{task.title}」を登録しました。")
return redirect("tasks:list")
else:
form = TaskForm()
return render(request, "tasks/task_form.html", {"form": form})
def task_detail(request, pk):
task = get_object_or_404(Task, pk=pk)
return render(request, "tasks/task_detail.html", {"task": task})
@require_POST
def task_toggle(request, pk):
task = get_object_or_404(Task, pk=pk)
task.done = not task.done
task.save(update_fields=["done"])
return redirect("tasks:list")
URL はアプリの urls.py に書き、プロジェクトの urls.py から include でつなぎます。app_name と name を付けると、テンプレートやビューから tasks:list のように名前で URL を作れます。
# tasks/urls.py
app_name = "tasks"
urlpatterns = [
path("", views.task_list, name="list"),
path("new/", views.task_create, name="create"),
path("<int:pk>/", views.task_detail, name="detail"),
path("<int:pk>/toggle/", views.task_toggle, name="toggle"),
]
# config/urls.py
urlpatterns = [
path("admin/", admin.site.urls),
path("tasks/", include("tasks.urls")),
]
| 開いた URL | 結果 | 理由 |
|---|---|---|
/tasks/ |
200 | 一覧 |
/tasks/999/ |
404「No Task matches the given query.」 | get_object_or_404 |
/tasks/1/toggle/(GET) |
405・Allow: POST |
require_POST |
/ |
404(URL の一覧が出る) | どのパターンにも当たらない |
DEBUG=True の404画面には「Using the URLconf defined in config.urls, Django tried these URL patterns」と、試した URL のパターンが並びます。URL のつなぎ忘れはこの画面で分かります。
同じことをクラスベースビューで書く
一覧と登録は、Django の汎用ビュー(ListView・CreateView)でも書けます。
from django.urls import reverse_lazy
from django.views.generic import CreateView, ListView
from .forms import TaskForm
from .models import Task
class TaskListView(ListView):
model = Task
context_object_name = "tasks" # テンプレートでの名前を合わせる
class TaskCreateView(CreateView):
model = Task
form_class = TaskForm
success_url = reverse_lazy("tasks:list")
テンプレート名を書かなくても200で表示されました。ListView は tasks/task_list.html、CreateView は tasks/task_form.html を既定で探すため、関数ビュー用に作ったテンプレートがそのまま使われています。
関数ビュー(FBV)
処理の流れが上から読める
GET と POST の分岐を自分で書く
入門ではまずこちら
クラスベースビュー(CBV)
一覧・詳細・登録・更新・削除が数行で済む
既定の名前(テンプレート・変数)を知っている必要がある
同じ形の画面が多いときに向く
テンプレートの使い方(継承・自動エスケープ)
この節では、画面の共通部分をまとめる書き方と、Django が自動で行う HTML のエスケープが分かります。
共通の枠を base.html に書き、各画面は {% extends %} と {% block %} で中身だけ書きます。テンプレートはアプリの templates/tasks/ に置きました(APP_DIRS=True のため設定の追加は要りません)。
<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>{% block title %}タスク{% endblock %}</title>
</head>
<body>
<header><a href="{% url 'tasks:list' %}">タスク一覧</a> | <a href="{% url 'tasks:create' %}">新規登録</a></header>
{% for message in messages %}<p class="msg">{{ message }}</p>{% endfor %}
<main>{% block content %}{% endblock %}</main>
</body>
</html>
{% extends "tasks/base.html" %}
{% block title %}タスク一覧{% endblock %}
{% block content %}
<h1>タスク一覧({{ tasks|length }}件)</h1>
<ul>
{% for task in tasks %}
<li>
<a href="{% url 'tasks:detail' task.pk %}">{{ task.title }}</a>
{% if task.due %}(期限 {{ task.due|date:"n/j" }}){% endif %}
{% if task.done %}済{% endif %}
<form method="post" action="{% url 'tasks:toggle' task.pk %}" style="display:inline">
{% csrf_token %}<button>{% if task.done %}戻す{% else %}完了{% endif %}</button>
</form>
</li>
{% empty %}
<li>タスクはありません。</li>
{% endfor %}
</ul>
{% endblock %}
| 書き方 | 意味 | 例 |
|---|---|---|
{{ 変数 }} |
値を表示(自動でエスケープ) | {{ task.title }} |
{% for %}…{% empty %} |
くり返し。0件のときの表示 | 「タスクはありません。」 |
{% url %} |
URL を名前から作る | {% url 'tasks:detail' task.pk %} |
| フィルター | 表示の形を変える | date:"n/j" → 10/30 |
{% csrf_token %} |
POST のフォームに必ず入れる | 隠しフィールドが出る |
タイトルに <b>太字?</b> を登録して一覧を開くと、HTML としては動かず、次のように文字として出力されました。
<a href="/tasks/5/"><b>太字?</b></a>
Django のテンプレートは、変数を表示するときに < などを自動でエスケープします。入力をそのまま HTML として出したいときに使う |safe は、信頼できる値にだけ使います。Django 6.0 からは、テンプレートの一部に名前を付けて使い回す {% partialdef %} も標準に入りました(6.0 のリリースノート)。
フォームの使い方(ModelForm・入力チェック・CSRF)
この節では、モデルからフォームを作り、入力チェックとエラー表示、CSRF 対策まで動かした結果が分かります。
ModelForm を使うと、モデルの定義から入力欄と入力チェックが作られます。独自のチェックは clean_項目名 に書きます。
from django import forms
from .models import Task
class TaskForm(forms.ModelForm):
class Meta:
model = Task
fields = ["title", "memo", "due", "priority"]
widgets = {"due": forms.DateInput(attrs={"type": "date"})}
def clean_title(self):
title = self.cleaned_data["title"].strip()
if title.startswith("test"):
raise forms.ValidationError("「test」で始まるタイトルは登録できません。")
return title
{{ form.as_p }} が出力した HTML の一部です。max_length=100 から maxlength="100" required が付きました。
<p> <label for="id_title">タイトル:</label> <input type="text" name="title" maxlength="100" required="" id="id_title"> </p>
<p> <label for="id_due">期限:</label> <input type="date" name="due" id="id_due"> </p>
| 操作(Chromium) | 結果 |
|---|---|
| タイトルを空のまま送信 | ブラウザが止めた(required)。サーバーには送られない |
| 「test です」で送信 | 同じ画面に「「test」で始まるタイトルは登録できません。」(ul.errorlist) |
| 正しく入れて送信 | 302 で一覧へ。「「資料を印刷する」を登録しました。」と表示、4件に |
| 一覧の「完了」を押す | POST で切り替え、一覧の並びが変わった(完了は下へ) |
| curl でトークン無しに POST | 403「CSRF検証に失敗したため、リクエストは中断されました。」 |
CSRF トークンを付けずに POST したときの、サーバーのログです。
Forbidden (CSRF cookie not set.): /tasks/new/
[29/Oct/2026 20:24:41] "POST /tasks/new/ HTTP/1.1" 403 3130
登録に成功したあとに redirect で一覧へ移しているのは、再読み込みで二重に登録されるのを防ぐためです(POST のあとにリダイレクトする形)。CSRF の仕組みと SameSite Cookie との関係は、関連記事「CSRF対策とSameSite|Cookie送信をChromeで実測し多層防御を実装」で扱っています。
テストを書いて動かす(manage.py test)
この節では、Django のテストの書き方と、実行したときの出力が分かります。
Django の TestCase は、テストのたびに空のテスト用 DB を作り、テストクライアントで画面を開けます。4つのテストを書きました。
from django.test import TestCase
from django.urls import reverse
from .models import Task
class TaskViewTests(TestCase):
def test_list_shows_tasks(self):
Task.objects.create(title="牛乳を買う")
res = self.client.get(reverse("tasks:list"))
self.assertEqual(res.status_code, 200)
self.assertContains(res, "牛乳を買う")
def test_create_redirects_and_saves(self):
res = self.client.post(reverse("tasks:create"), {"title": "新しい作業", "priority": 1})
self.assertRedirects(res, reverse("tasks:list"))
self.assertEqual(Task.objects.get().title, "新しい作業")
def test_title_starting_with_test_is_rejected(self):
res = self.client.post(reverse("tasks:create"), {"title": "test です", "priority": 2})
self.assertEqual(res.status_code, 200)
self.assertContains(res, "「test」で始まるタイトルは登録できません。")
self.assertEqual(Task.objects.count(), 0)
def test_toggle_requires_post(self):
task = Task.objects.create(title="x")
res = self.client.get(reverse("tasks:toggle", args=[task.pk]))
self.assertEqual(res.status_code, 405)
$ python manage.py test
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
....
----------------------------------------------------------------------
Ran 4 tests in 0.027s
OK
Destroying test database for alias 'default'...
テスト用の DB はメモリー上に作られ(file:memorydb_default?mode=memory)、開発用の db.sqlite3 には触れませんでした。
本番前のcheck --deployとデプロイの選択肢
この節では、本番に出す前に Django が教えてくれる設定の不足と、開発サーバーの代わりに使うものが分かります。
python manage.py check --deploy は、本番に向かない設定を一覧にしてくれます。startproject のままの設定で実行した原文です(Django 6.1.2)。
$ python manage.py check --deploy
SystemCheckError: System check identified some issues:
ERRORS:
?: (mail.E001) Your MAILERS setting uses a development-only email backend in the 'default' entry (django.core.mail.backends.console.EmailBackend).
HINT: Use a production-ready email backend, such as the SMTP backend, otherwise email will not be sent.
WARNINGS:
?: (security.W004) You have not set a value for the SECURE_HSTS_SECONDS setting. …
?: (security.W008) Your SECURE_SSL_REDIRECT setting is not set to True. …
?: (security.W009) Your SECRET_KEY has less than 50 characters, less than 5 unique characters, or it's prefixed with 'django-insecure-' …
?: (security.W012) SESSION_COOKIE_SECURE is not set to True. …
?: (security.W016) You have 'django.middleware.csrf.CsrfViewMiddleware' in your MIDDLEWARE, but you have not set CSRF_COOKIE_SECURE to True. …
?: (security.W018) You should not have DEBUG set to True in deployment.
?: (security.W020) ALLOWED_HOSTS must not be empty in deployment.
System check identified 8 issues (0 silenced).
SECRET_KEY・DEBUG・ALLOWED_HOSTS を環境変数から読むようにし、本番だけ HTTPS の設定を有効にしました。
SECRET_KEY = os.environ.get("DJANGO_SECRET_KEY", "django-insecure-dev-only")
DEBUG = os.environ.get("DJANGO_DEBUG", "1") == "1"
ALLOWED_HOSTS = os.environ.get("DJANGO_ALLOWED_HOSTS", "127.0.0.1,localhost").split(",")
STATIC_ROOT = BASE_DIR / "staticfiles"
# settings.py の最後に置く(MAILERS より前に書くと上書きされる)
if not DEBUG:
SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SECURE_HSTS_SECONDS = 3600
MAILERS = {"default": {"BACKEND": "django.core.mail.backends.smtp.EmailBackend"}}
最初はこの if を settings.py の途中に書いたため、後ろにある startproject の MAILERS で上書きされ、mail.E001 が消えませんでした。最後に移すと ERRORS は0件になり、残りは HSTS のサブドメインと preload の2件の警告(W005・W021)だけでした。HSTS は一度有効にすると戻しにくいので、公式ドキュメントを読んでから値を決めます。
- 1DEBUG を False にするTrue のままだとエラー画面に設定やコードの一部が出る
- 2ALLOWED_HOSTS に本番のドメインを書く空のまま DEBUG=False で起動すると CommandError で止まった
- 3SECRET_KEY を作り直し、コードに書かないget_random_secret_key() で作り、環境変数などで渡す
- 4collectstatic で静的ファイルを集める手元では 130 ファイル・1.8MB が STATIC_ROOT にコピーされた
- 5runserver ではなく WSGI・ASGI のサーバーで動かす公式は runserver を本番に向かない軽量な開発用サーバーとしている
Gunicorn で起動した結果
公式ドキュメントが挙げる WSGI のサーバーは Gunicorn・uWSGI・Granian・Apache の mod_wsgi、ASGI のサーバーは Daphne・Hypercorn・Uvicorn・Granian です。Gunicorn を venv に入れ、DEBUG=False で 127.0.0.1 に起動しました。
$ gunicorn config.wsgi:application -b 127.0.0.1:45736 -w 2
[INFO] Starting gunicorn 26.2.0
[INFO] Listening at: http://127.0.0.1:45736 (3791)
[INFO] Using worker: sync
[INFO] Booting worker with pid: 3794
[INFO] Booting worker with pid: 3795
$ curl -sS -I http://127.0.0.1:45736/tasks/
HTTP/1.1 301 Moved Permanently
Location: https://127.0.0.1:45736/tasks/
SECURE_SSL_REDIRECT が効いて、HTTP は HTTPS へ301で転送されました。前に置いたリバースプロキシ(nginx など)が HTTPS を受ける構成では、X-Forwarded-Proto: https を付けると200になりました(Gunicorn は既定で 127.0.0.1 からのこのヘッダーを信頼する)。同じ一覧ページに1本ずつ500回送った速さは、runserver が174〜210 req/s、Gunicorn(2ワーカー)が146〜165 req/s で、この小さなアプリでは大きな差は出ませんでした。runserver を本番に使わない理由は速さではなく、公式がセキュリティと性能の検査を受けていない開発用と位置づけているためです。
Djangoのよくあるエラーと直し方(原文つき)
この節では、入門でつまずきやすいエラーの文言と、その直し方が分かります。
この記事のアプリを作る途中で、実際に出したエラーです。
| 出た文言(原文) | 原因 | 直し方 |
|---|---|---|
ImportError: Couldn't import Django. … Did you forget to activate a virtual environment? |
venv の外の Python で manage.py を動かした | venv を有効にする |
No changes detected(makemigrations) |
アプリを INSTALLED_APPS に入れていない | settings.py に "tasks" を足す |
No installed app with label 'tasks'. |
同上(アプリ名を指定したとき) | 同上 |
OperationalError: no such column: tasks_task.priority |
モデルを変えて migrate していない | makemigrations → migrate |
TemplateDoesNotExist: tasks/task_lis.html |
テンプレートの名前・置き場所の誤り | アプリ/templates/アプリ名/ に置く |
NoReverseMatch: Reverse for 'detail' with no arguments not found. |
URL に要る引数(pk)を渡していない | {% url 'tasks:detail' task.pk %} |
NoReverseMatch: Reverse for 'detail' not found. |
app_name の名前空間を付け忘れた | tasks:detail と書く |
403 CSRF検証に失敗したため、リクエストは中断されました。 |
フォームに {% csrf_token %} が無い |
form の中に入れる |
DisallowedHost: Invalid HTTP_HOST header: 'example.com'. |
ALLOWED_HOSTS に無いホスト名で開いた(400) | ALLOWED_HOSTS に足す |
CommandError: You must set settings.ALLOWED_HOSTS if DEBUG is False. |
DEBUG=False で ALLOWED_HOSTS が空 | 本番のドメインを書く |
Error: That port is already in use. |
同じポートで runserver が動いている | 前のサーバーを止めるか、別のポート |
You appear not to have the 'sqlite3' program installed |
dbshell に要る sqlite3 コマンドが無い | sqlite3 を入れるか、shell の ORM で見る |
マイグレーションのつまずき
migrations フォルダーは Git に入れてチームで共有する
本番の設定
"*" を入れて済ませるDEBUG=True のエラー画面には設定やスタックトレースが出る
Flask・FastAPIとの違いと判断表
この節では、Django と Flask・FastAPI のどれを選ぶかの目安が分かります。
Django は「必要な部品が最初から揃っている」、Flask は「小さく始めて必要な部品を足す」、FastAPI は「型ヒントで API を作る」フレームワークです。
| 観点 | Django | Flask | FastAPI |
|---|---|---|---|
| DB・ORM | 標準(マイグレーションつき) | 外部(SQLAlchemy など) | 外部(SQLAlchemy・SQLModel など) |
| 管理画面 | 標準 | 外部 | 外部 |
| ログイン・権限 | 標準(auth) | 外部 | 自分で組む・外部 |
| フォーム・CSRF | 標準 | 外部(Flask-WTF など) | API 中心のため用途が違う |
| API の自動ドキュメント | DRF・Django Ninja を足す | 外部 | 標準(OpenAPI) |
| 作りたいもの | 向いているもの |
|---|---|
| ログイン・管理画面・DB を使う業務アプリや会員サイト | Django |
| 画面が数枚の小さなアプリ・試作 | Flask |
| フロントエンドやスマホアプリから呼ぶ JSON の API | FastAPI(Django なら DRF・Ninja) |
| 社内の管理ツール(データの一覧と編集が中心) | Django(管理画面だけで足りることもある) |
| 非同期の処理が中心の API | FastAPI |
同じ「フォームで登録 → 一覧」を Flask・FastAPI・Django の3つで作り、コード量と速さを並べた記事「PythonでWebアプリを作る|Flask・FastAPI・Django実測比較」も用意しています。FastAPI で API を作る手順は、関連記事「FastAPI入門|uvで作るCRUD API完成コードと実行結果」で扱っています。
Django を学ぶ順番
- 1Python の基本と venvクラス・関数・pip
- 2この記事の流れでアプリを1本作るモデル → 管理画面 → ビュー → テンプレート → フォーム
- 3公式チュートリアル(その1〜8)テスト・静的ファイル・管理画面のカスタマイズまで
- 4ログイン(auth のビュー)と権限LoginView・login_required
- 5PostgreSQL とデプロイcheck --deploy・Gunicorn・リバースプロキシ
- 6API(DRF・Django Ninja)フロントエンドと分けるとき
よくある質問
Q. Djangoの読み方は? A. 「ジャンゴ」です。公式 FAQ は「JANG-oh」と書き、D は発音しないとしています。
Q. Djangoは無料ですか?商用で使えますか? A. 無料のオープンソースで、BSD ライセンスです。商用のサービスにも使えます。
Q. Djangoはフロントエンドですか、バックエンドですか? A. サーバー側(バックエンド)のフレームワークです。テンプレートで HTML を作って返せるため、画面までを Django だけで作ることもできます。
Q. Django入門に必要な Python の版は? A. Django 6.1 は Python 3.12・3.13・3.14(6.1.3 から 3.15)に対応しています。3.10・3.11 なら 5.2 LTS を使います。
Q. プロジェクトとアプリの違いは? A. プロジェクトはサイト全体の設定(settings.py・urls.py)、アプリは「タスク」「ブログ」のような機能のまとまりです。1つのプロジェクトに複数のアプリを入れられます。
Q. makemigrations と migrate の違いは? A. makemigrations はモデルの変更から「変更手順のファイル」を作り、migrate はそれを DB に反映します。中身の SQL は sqlmigrate で見られます。
Q. データベースは SQLite のままでよいですか? A. 学習や小さなアプリでは SQLite で動きます。複数の人が同時に書き込む本番では、PostgreSQL などを使うことが多いです。Django 6.1 は PostgreSQL 15 以上に対応しています。
Q. runserver を本番で使えますか? A. 使いません。公式ドキュメントは runserver を本番に向かない軽量な開発用サーバーとし、WSGI・ASGI のサーバー(Gunicorn・Uvicorn など)で動かす方法を案内しています。
Q. 管理画面を一般のユーザーに使わせてよいですか? A. 管理画面はスタッフ(is_staff)向けの機能です。一般のユーザー向けの画面は、ビューとテンプレートで作ります。
Q. Django で API は作れますか? A. 作れます。JsonResponse で JSON を返すこともでき、本格的には Django REST framework や Django Ninja を足します。
Q. Django は難しいですか? A. 最初に覚える決まり(ファイルの置き場所・名前)が多めです。その代わり、決まりどおりに書くと管理画面・フォーム・認証が少ないコードで動きます。
まとめ
- Django は Python の Web フレームワークで、ORM・マイグレーション・管理画面・認証・フォーム・セキュリティ対策が標準で入っている。2026年10月29日時点の最新は 6.1.2、LTS は 5.2。
- 使い方の流れは startproject → startapp(INSTALLED_APPS に登録)→ モデル → makemigrations・migrate → 管理画面 → ビュー・URL → テンプレート → フォーム。手元では177行でタスク管理アプリが動いた。
- sqlmigrate で、Django が流す SQL を反映の前に見られる。SQLite で列を足すと、テーブルを作り直す SQL になった。
- テンプレートは自動でエスケープし、POST のフォームは CSRF トークンが無いと403になる。
- 本番の前に check --deploy の ERRORS を0件にし、DEBUG・ALLOWED_HOSTS・SECRET_KEY を直して、Gunicorn などで動かす。
参考・出典
確認日はいずれも 2026年10月29日です。
- Django「Download」(Supported versions):https://www.djangoproject.com/download/
- Django ドキュメント「はじめに」(チュートリアル その1〜8):https://docs.djangoproject.com/ja/6.1/intro/
- Django「FAQ: Installation」(What Python version can I use with Django?):https://docs.djangoproject.com/en/6.1/faq/install/
- Django「FAQ: General」(MTV・名前・読み方):https://docs.djangoproject.com/en/6.1/faq/general/
- Django「Django 6.1 release notes」:https://docs.djangoproject.com/en/6.1/releases/6.1/
- Django「Django 6.0 release notes」:https://docs.djangoproject.com/en/6.1/releases/6.0/
- Django「How to deploy Django」:https://docs.djangoproject.com/en/6.1/howto/deployment/
- Django「Deployment checklist」:https://docs.djangoproject.com/en/6.1/howto/deployment/checklist/
- Django「django-admin and manage.py」:https://docs.djangoproject.com/en/6.1/ref/django-admin/
- Django のソースコード「LICENSE」(BSD ライセンス):https://github.com/django/django/blob/main/LICENSE
- Python「venv — 仮想環境の作成」:https://docs.python.org/ja/3/library/venv.html
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Djangoとは・使い方入門|6.1でアプリを1本作る全コマンドと出力」
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。