1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Django入門 — LaravelエンジニアがDjangoを触ってみた

1
Posted at

はじめに

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の.envconfig/を合わせたようなファイル。アプリを作ったら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が向いている
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?