DjangoでWeb開発を始めたきっかけ
PythonでWeb開発をするなら、フレームワーク選びは避けて通れないですね。自分の場合、最初はFastAPIで作るREST API入門でも書いたようにFastAPIから入ったんですが、管理画面やユーザー認証を一から作るのが面倒になってきて、Djangoに手を出しました。
この記事では、Django 5.1(Python 3.12)を使って、プロジェクトの初期構成からCRUD実装までを一通り解説します。「Djangoって重そう」と思っている方にこそ読んでほしい内容です。
DjangoとFastAPIの違い
フルスタック vs マイクロフレームワーク
まず、DjangoとFastAPIの立ち位置を整理しておきます。
| 項目 | Django | FastAPI |
|---|---|---|
| タイプ | フルスタック | マイクロフレームワーク |
| ORM | 組み込み | なし(SQLAlchemy等を別途導入) |
| 管理画面 | 自動生成 | なし |
| 認証 | 組み込み | 自前実装が必要 |
| 非同期サポート | 部分的(ASGI対応) | ネイティブ |
| 学習コスト | やや高い | 低い |
APIだけ作るならFastAPIが向いていますが、管理画面・認証・フォームバリデーションまで必要なプロジェクトではDjangoの方が圧倒的に速いです。
よくある間違い:全部Djangoでやろうとする
Djangoはなんでもできるがゆえに、APIもフロントもDjangoテンプレートで作ろうとしてしまうケースがあります。今の時代、フロントはNext.jsやVueに任せて、DjangoはAPIサーバー + 管理画面に徹する構成がおすすめです。
プロジェクトの初期セットアップ
インストールとプロジェクト作成
python -m venv .venv
source .venv/bin/activate
pip install django==5.1
django-admin startproject mysite .
python manage.py startapp blog
(.venv) $ django-admin startproject mysite .
(.venv) $ python manage.py startapp blog
ディレクトリ構成
作成直後のディレクトリ構成はこうなります。
.
├── blog/
│ ├── admin.py
│ ├── apps.py
│ ├── models.py
│ ├── tests.py
│ └── views.py
├── mysite/
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
└── manage.py
mysite/がプロジェクト設定、blog/がアプリケーション。Djangoは1プロジェクト内に複数アプリを持てる設計になっています。
settings.pyの初期設定
忘れがちなのがINSTALLED_APPSへのアプリ登録です。
# mysite/settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'blog', # ここを追加
]
# 追加しないとモデルのマイグレーションが認識されない
モデル定義とマイグレーション
NGパターン:型を意識しないモデル設計
まずやりがちなNGパターンから。
# NG: フィールドの制約が甘い
class Post(models.Model):
title = models.CharField(max_length=500) # 500は長すぎる
body = models.TextField()
created = models.DateTimeField() # auto_now_addを使うべき
タイトルに500文字も許容すると、テンプレートやAPIレスポンスで表示が崩れる原因になります。auto_now_addを使わないと、毎回手動で日時をセットする必要が出てきます。
OKパターン:制約を適切に設定
# blog/models.py
from django.db import models
class Post(models.Model):
title = models.CharField(max_length=200)
slug = models.SlugField(unique=True)
body = models.TextField()
published = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
ordering = ['-created_at']
def __str__(self):
return self.title
python manage.py makemigrations
python manage.py migrate
Migrations for 'blog':
blog/migrations/0001_initial.py
- Create model Post
Operations to perform:
Apply all migrations: admin, auth, blog, contenttypes, sessions
Running migrations:
Applying blog.0001_initial... OK
ordering = ['-created_at']をMetaに設定しておくと、クエリセットがデフォルトで新しい順になります。地味に便利です。
管理画面のカスタマイズ
admin.pyの設定
Djangoの管理画面は、2行書くだけで使えるようになります。ここが他のフレームワークにはない強みですね。
# blog/admin.py
from django.contrib import admin
from .models import Post
@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
list_display = ('title', 'published', 'created_at')
list_filter = ('published',)
search_fields = ('title', 'body')
prepopulated_fields = {'slug': ('title',)}
python manage.py createsuperuser
python manage.py runserver
Username: admin
Email address: admin@example.com
Password: ********
Superuser created successfully.
Starting development server at http://127.0.0.1:8000/
http://127.0.0.1:8000/admin/にアクセスすると、Post一覧の検索・フィルタ・一括操作が全て使えます。公式ドキュメントによると、prepopulated_fieldsはJavaScriptでリアルタイムにslugを自動生成してくれる機能です。
ビューとURLの設定
関数ビュー vs クラスベースビュー
Djangoには関数ビュー(FBV)とクラスベースビュー(CBV)の2つがあります。
| 項目 | 関数ビュー | クラスベースビュー |
|---|---|---|
| シンプルさ | 直感的 | やや抽象的 |
| 再利用性 | 低い | 高い(Mixin活用) |
| CRUD定型処理 | 毎回書く | 汎用ビューで省略可 |
| おすすめ | 小規模・学習 | 本番プロジェクト |
クラスベースビューでCRUDを実装
# blog/views.py
from django.views.generic import ListView, DetailView, CreateView
from django.urls import reverse_lazy
from .models import Post
class PostListView(ListView):
model = Post
queryset = Post.objects.filter(published=True)
template_name = 'blog/post_list.html'
context_object_name = 'posts'
paginate_by = 10
class PostDetailView(DetailView):
model = Post
template_name = 'blog/post_detail.html'
class PostCreateView(CreateView):
model = Post
fields = ['title', 'slug', 'body', 'published']
template_name = 'blog/post_form.html'
success_url = reverse_lazy('post_list')
# blog/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.PostListView.as_view(), name='post_list'),
path('post/<slug:slug>/', views.PostDetailView.as_view(), name='post_detail'),
path('post/new/', views.PostCreateView.as_view(), name='post_create'),
]
# mysite/urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('blog.urls')),
]
ListViewのpaginate_by = 10だけで、ページネーションが自動で付きます。FastAPIだとこれを自前実装する必要があるので、こういうところがDjangoの強みだと思います。
テストの書き方
モデルとビューのテスト
DjangoはTestCaseクラスでテスト用DBが自動で用意されるので、テストが書きやすいです。
# blog/tests.py
from django.test import TestCase, Client
from django.urls import reverse
from .models import Post
class PostModelTest(TestCase):
def setUp(self):
self.post = Post.objects.create(
title='テスト記事',
slug='test-post',
body='テスト本文',
published=True,
)
def test_str_representation(self):
self.assertEqual(str(self.post), 'テスト記事')
def test_ordering(self):
post2 = Post.objects.create(
title='2番目の記事', slug='second', body='本文', published=True
)
posts = list(Post.objects.all())
self.assertEqual(posts[0], post2) # 新しい順
class PostViewTest(TestCase):
def setUp(self):
self.client = Client()
self.post = Post.objects.create(
title='公開記事', slug='public', body='本文', published=True
)
def test_list_view(self):
response = self.client.get(reverse('post_list'))
self.assertEqual(response.status_code, 200)
self.assertContains(response, '公開記事')
def test_unpublished_not_shown(self):
Post.objects.create(
title='下書き', slug='draft', body='本文', published=False
)
response = self.client.get(reverse('post_list'))
self.assertNotContains(response, '下書き')
python manage.py test blog
Creating test database for alias 'default'...
....
----------------------------------------------------------------------
Ran 4 tests in 0.032s
OK
Destroying test database for alias 'default'...
テスト用DBは各テストケースごとにリセットされるので、テスト間の依存関係を気にしなくていいです。
まとめ
- DjangoはORM・管理画面・認証が組み込みで、CRUDアプリを高速に構築できる
- APIだけならFastAPI、管理画面含む本格アプリならDjangoが向いている
- モデル設計では
auto_now_addやフィールド制約を意識する - クラスベースビューの汎用ビューを使えば、定型CRUDはほぼコードを書かずに実装可能
TestCaseで自動的にテスト用DBが用意されるため、テストの敷居が低い
よくある質問(FAQ)
DjangoとFlaskはどちらを選ぶべきですか?
Flaskは軽量で学習コストが低いですが、認証やORMは自分で選んで組み合わせる必要があります。管理画面やユーザー管理が必要なプロジェクトならDjango、小さなAPIサーバーならFlaskが適しています。
Django REST Frameworkは必要ですか?
APIを提供するなら、Django REST Framework(DRF)の導入を強くおすすめします。シリアライザ・ビューセット・認証の仕組みが揃っていて、生のDjangoビューでJSONを返すより保守性が格段に上がります。
Djangoのパフォーマンスは大丈夫ですか?
InstagramやPinterestなどの大規模サービスでも使われているので、パフォーマンス自体は問題ありません。ボトルネックになるのはN+1クエリや不要なJOINで、select_related()とprefetch_related()を適切に使えば対処できます。

