DjangoでWeb開発を始めるには?プロジェクト構成とCRUD実装のコツ

DjangoでWeb開発を始めるには?プロジェクト構成とCRUD実装のコツ | mohablog
目次

DjangoでWeb開発を始めたきっかけ

PythonでWeb開発をするなら、フレームワーク選びは避けて通れないですね。自分の場合、最初はFastAPIで作るREST API入門でも書いたようにFastAPIから入ったんですが、管理画面やユーザー認証を一から作るのが面倒になってきて、Djangoに手を出しました。

この記事では、Django 5.1(Python 3.12)を使って、プロジェクトの初期構成からCRUD実装までを一通り解説します。「Djangoって重そう」と思っている方にこそ読んでほしい内容です。

DjangoとFastAPIの違い

フルスタック vs マイクロフレームワーク

まず、DjangoとFastAPIの立ち位置を整理しておきます。

項目DjangoFastAPI
タイプフルスタックマイクロフレームワーク
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')),
]

ListViewpaginate_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()を適切に使えば対処できます。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次