はじめに
FlaskとFastAPIを触ったので、次はDjango。
PythonのWebフレームワークの中で一番歴史が長くて機能が多い。「バッテリー同梱(batteries included)」という設計思想で、認証・ORM・管理画面・マイグレーション・テンプレートエンジンが最初から全部入っている。
LaravelもフルスタックフレームワークでDjangoと対比しやすいので、Laravel経験者視点で整理した。
インストールとプロジェクト作成
pip install django
# プロジェクトを作成
django-admin startproject myproject
# アプリを作成(Laravelのモジュールに相当)
cd myproject
python manage.py startapp users
生成されるディレクトリ構造:
myproject/
├── manage.py # Laravelのartisanに相当
├── myproject/
│ ├── __init__.py
│ ├── settings.py # Laravelのconfig/以下に相当
│ ├── urls.py # Laravelのroutes/web.phpに相当
│ ├── asgi.py
│ └── wsgi.py
└── users/ # アプリ(機能ごとに分割)
├── __init__.py
├── admin.py # 管理画面の設定
├── apps.py
├── migrations/ # Laravelのdatabase/migrationsに相当
├── models.py # Laravelのapp/Modelsに相当
├── tests.py
└── views.py # Laravelのapp/Http/Controllersに相当
LaravelはMVCだがDjangoは**MTV(Model-Template-View)**という構造。名前が違うだけで概念は近い。
| Laravel | Django |
|---|---|
| Model | Model |
| View(blade) | Template |
| Controller | View |
| routes/web.php | urls.py |
| artisan | manage.py |
「DjangoのViewはコントローラーです」というのを最初に把握しておくと混乱が減る。
開発サーバーの起動
python manage.py runserver
# http://127.0.0.1:8000/ で起動
Laravelのphp artisan serveに相当。
settings.py — 設定ファイル
# myproject/settings.py(抜粋)
SECRET_KEY = "django-insecure-xxxx" # 本番では環境変数から読む
DEBUG = True
ALLOWED_HOSTS = ["localhost", "127.0.0.1"]
# インストール済みアプリ(作ったアプリを必ず追加する)
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"users", # ← 作ったアプリを追加
]
# データベース設定
DATABASES = {
"default": {
"ENGINE": "django.db.backends.postgresql",
"NAME": "mydb",
"USER": "user",
"PASSWORD": "password",
"HOST": "localhost",
"PORT": "5432",
}
}
# 言語・タイムゾーン
LANGUAGE_CODE = "ja"
TIME_ZONE = "Asia/Tokyo"
USE_I18N = True
USE_TZ = True
Laravelの.envとconfig/を合わせたようなファイル。アプリを作ったらINSTALLED_APPSに追加しないとマイグレーションが動かない。これを忘れて「なんでテーブルが作られないんだ」と詰まった。
モデルの定義
# users/models.py
from django.db import models
class User(models.Model):
name = models.CharField(max_length=50)
email = models.EmailField(unique=True)
age = models.IntegerField(null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
is_active = models.BooleanField(default=True)
class Meta:
db_table = "users" # テーブル名
ordering = ["-created_at"] # デフォルトの並び順
def __str__(self):
return f"{self.name} ({self.email})"
PHPのEloquentと比べると:
<?php
// Laravel Eloquent
class User extends Model
{
protected $fillable = ["name", "email", "age"];
protected $casts = ["created_at" => "datetime"];
}
Laravelはマイグレーションとモデルが分離しているが、Djangoはモデルにカラムの型情報を直接書く。モデルを変更するとマイグレーションが自動生成される設計。
よく使うフィールドタイプ
models.CharField(max_length=100) # VARCHAR
models.TextField() # TEXT
models.IntegerField() # INTEGER
models.FloatField() # FLOAT
models.BooleanField(default=True) # BOOLEAN
models.DateField() # DATE
models.DateTimeField(auto_now_add=True)# TIMESTAMP(作成時)
models.DateTimeField(auto_now=True) # TIMESTAMP(更新時)
models.EmailField() # VARCHAR(メール形式バリデーション)
models.URLField() # VARCHAR(URL形式)
models.ForeignKey("Post", on_delete=models.CASCADE) # 外部キー
models.ManyToManyField("Tag") # 多対多
マイグレーション
# マイグレーションファイルを生成(Laravelのmake:migrationに相当)
python manage.py makemigrations
# マイグレーションを実行(Laravelのmigrate)
python manage.py migrate
# マイグレーションの状態を確認
python manage.py showmigrations
# SQLを確認(実行はしない)
python manage.py sqlmigrate users 0001
Laravelと違う点として、Djangoはモデルから自動でマイグレーションを生成する。models.pyを変更してmakemigrationsを実行すれば差分が自動で作られる。Laravelは自分でマイグレーションファイルを書く必要がある。
# 生成されるマイグレーションファイル
users/migrations/
└── 0001_initial.py # 自動生成される
# 0001_initial.py(自動生成の中身)
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = []
operations = [
migrations.CreateModel(
name="User",
fields=[
("id", models.BigAutoField(primary_key=True)),
("name", models.CharField(max_length=50)),
("email", models.EmailField(unique=True)),
("created_at", models.DateTimeField(auto_now_add=True)),
],
),
]
ORM — クエリの書き方
from users.models import User
# 全件取得(LaravelのUser::all())
users = User.objects.all()
# 条件で絞り込み(LaravelのUser::where())
active_users = User.objects.filter(is_active=True)
young_users = User.objects.filter(age__lt=30) # age < 30
tokyo_users = User.objects.filter(name__contains="田") # LIKE '%田%'
# 1件取得(Laravelのfind()、findOrFail())
user = User.objects.get(id=1) # 見つからなければDoesNotExist例外
user = User.objects.filter(id=1).first() # 見つからなければNone
# ソート
users = User.objects.order_by("-created_at") # -で降順
# 件数
count = User.objects.filter(is_active=True).count()
# 作成(Laravelのcreate())
user = User.objects.create(name="田中", email="tanaka@example.com")
# 更新
User.objects.filter(id=1).update(name="田中太郎")
# 削除
User.objects.filter(id=1).delete()
Laravelと比べるとUser::where("is_active", true)がUser.objects.filter(is_active=True)になる。ルックアップ記法(__lt、__containsなど)が最初は独特に感じたが慣れると読みやすい。
よく使うルックアップ
filter(age__lt=30) # age < 30
filter(age__lte=30) # age <= 30
filter(age__gt=20) # age > 20
filter(age__gte=20) # age >= 20
filter(name__contains="田") # LIKE '%田%'
filter(name__startswith="田") # LIKE '田%'
filter(name__in=["田中", "鈴木"]) # IN ('田中', '鈴木')
filter(age__isnull=True) # IS NULL
ビュー(View) — コントローラーに相当
DjangoのViewはリクエストを受け取ってレスポンスを返す関数またはクラス。
関数ベースビュー(FBV)
# users/views.py
from django.http import JsonResponse
from django.views.decorators.http import require_http_methods
import json
from .models import User
@require_http_methods(["GET"])
def user_list(request):
users = User.objects.all().values("id", "name", "email")
return JsonResponse(list(users), safe=False)
@require_http_methods(["GET"])
def user_detail(request, user_id):
try:
user = User.objects.get(id=user_id)
return JsonResponse({"id": user.id, "name": user.name})
except User.DoesNotExist:
return JsonResponse({"error": "見つかりません"}, status=404)
@require_http_methods(["POST"])
def user_create(request):
data = json.loads(request.body)
user = User.objects.create(
name=data["name"],
email=data["email"],
)
return JsonResponse({"id": user.id, "name": user.name}, status=201)
クラスベースビュー(CBV)
from django.http import JsonResponse
from django.views import View
import json
from .models import User
class UserListView(View):
def get(self, request):
users = User.objects.all().values("id", "name", "email")
return JsonResponse(list(users), safe=False)
def post(self, request):
data = json.loads(request.body)
user = User.objects.create(**data)
return JsonResponse({"id": user.id}, status=201)
FBVはシンプルで読みやすく、CBVはCRUDのような定型処理をまとめやすい。Laravelのコントローラーはクラスが必須だが、DjangoはFBVで気軽に書けるのが好み。
URLルーティング
# users/urls.py
from django.urls import path
from . import views
urlpatterns = [
path("", views.user_list, name="user-list"),
path("<int:pk>/", views.user_detail, name="user-detail"),
path("create/", views.user_create, name="user-create"),
]
# myproject/urls.py(プロジェクトのルートURL)
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path("admin/", admin.site.urls),
path("users/", include("users.urls")), # アプリのURLを取り込む
]
LaravelのRoute::group(["prefix" => "users"])に相当するのがinclude()。アプリごとにURLを分割してルートで統合する設計はLaravelと同じ発想。
管理画面 — Djangoの最大の強み
DjangoのキラーフィーチャーといえばAdmin管理画面。
# users/admin.py
from django.contrib import admin
from .models import User
@admin.register(User)
class UserAdmin(admin.ModelAdmin):
list_display = ["id", "name", "email", "is_active", "created_at"]
list_filter = ["is_active"]
search_fields = ["name", "email"]
ordering = ["-created_at"]
# 管理ユーザーを作成
python manage.py createsuperuser
http://localhost:8000/admin/にアクセスすると、モデルのCRUDができる管理画面が自動で生成される。これだけで業務システムの管理画面として使えるレベル。
LaravelにはFlame(旧Voyager)などのパッケージがあるが、Djangoのadminほど標準で統合されていない。これはDjangoが圧倒的に優れている部分。
テンプレートエンジン
# views.py
from django.shortcuts import render
from .models import User
def user_list(request):
users = User.objects.all()
return render(request, "users/list.html", {"users": users})
<!-- templates/users/list.html -->
<!DOCTYPE html>
<html lang="ja">
<body>
<h1>ユーザー一覧</h1>
<ul>
{% for user in users %}
<li>{{ user.name }} — {{ user.email }}</li>
{% empty %}
<li>ユーザーがいません</li>
{% endfor %}
</ul>
</body>
</html>
Jinja2ライクな{% %}構文はLaravelのBladeに近い感覚。{% empty %}でリストが空のときの処理を書けるのは地味に便利。
Djangoのテスト
# users/tests.py
from django.test import TestCase, Client
from .models import User
class UserModelTest(TestCase):
def setUp(self):
User.objects.create(name="田中", email="tanaka@example.com")
def test_user_str(self):
user = User.objects.get(name="田中")
self.assertEqual(str(user), "田中 (tanaka@example.com)")
class UserViewTest(TestCase):
def setUp(self):
self.client = Client()
User.objects.create(name="田中", email="tanaka@example.com")
def test_user_list(self):
response = self.client.get("/users/")
self.assertEqual(response.status_code, 200)
def test_user_create(self):
import json
response = self.client.post(
"/users/create/",
data=json.dumps({"name": "鈴木", "email": "suzuki@example.com"}),
content_type="application/json",
)
self.assertEqual(response.status_code, 201)
python manage.py test
DjangoはデフォルトでPythonのunittestベースのテストが動く。テスト実行時はテスト用DBが自動で作られて終了後に削除される。pytest-djangoを使うとpytestで書けて、fixtureなども使える。
LaravelとDjangoの対応表
| Laravel | Django |
|---|---|
php artisan |
python manage.py |
make:model |
startapp後にmodels.pyに書く |
make:migration |
makemigrations(自動生成) |
migrate |
migrate |
make:controller |
views.pyに関数/クラスを書く |
routes/web.php |
urls.py |
config/ |
settings.py |
.env |
settings.py + 環境変数 |
php artisan tinker |
python manage.py shell |
storage/ |
MEDIA_ROOT |
public/ |
STATIC_ROOT |
php artisan serve |
runserver |
| Eloquent | Django ORM |
| Blade | Django Template |
| Gate/Policy | Permission/Group |
| 管理画面(別途) | Django Admin(標準) |
触ってみた感想
良かった部分
makemigrationsでモデルから自動でマイグレーションが生成されるのはLaravelより楽。カラムを追加するたびにマイグレーションファイルを手書きしなくていい。
Admin管理画面が数行で動くのは感動した。内部ツールや管理システムを作るなら最初からDjangoを選ぶ理由がある。
慣れが必要だった部分
MTV構造の「ViewがコントローラーでTemplateがビュー」という命名は最初に混乱する。Laravel慣れしているとどうしても「ViewはHTMLを返すやつ」という先入観がある。
INSTALLED_APPSに追加を忘れるミスを何度かやった。新しいアプリを作るたびに設定ファイルを触るのはLaravelにない手間。
まとめ
- DjangoはLaravelと同じ「フルスタック・バッテリー同梱」な思想
- MTV構造はMVCと概念は同じ、命名だけが違う
- モデルからマイグレーションを自動生成するのはLaravelより楽
- Admin管理画面はDjangoの最大の強み
- APIだけ作るならFastAPI、HTML込みのWebアプリやAdmin画面が必要ならDjangoが向いている