数年ぶりに Django チュートリアルをやってみた

こんにちは！久しぶりに Django のチュートリアルを写経してみたところ、いくつか変更点があり進化を感じました。

Django の公式チュートリアルは、フレームワークの基本を学ぶための優れた教材ですが、バージョンアップに伴い少しずつ内容も変化しています。私はメジャーバージョンが2か3の頃によく使っていましたが、何年もDjangoから離れていたためかなり久しぶりです。

今回は Django 5.1 のチュートリアルで見つけた主な変更点について紹介します。これから Django を始める方も、しばらく使っていなかった方も、参考にしてみてください。

## 1. INSTALLED_APPS への登録方法の変化

以前は、新しいアプリケーションを作成したときに `INSTALLED_APPS` に直接アプリケーション名を追加していました：

```python
# 昔の方法
INSTALLED_APPS = [
    'polls',  # アプリ名を直接文字列として指定
    'django.contrib.admin',
    # ...
]
```

しかし、現在の公式チュートリアルでは、各アプリのコンフィグクラスを明示的に使用することが推奨されています：

```python
# 現在の方法
INSTALLED_APPS = [
    'polls.apps.PollsConfig',  # アプリのコンフィグクラスを指定
    'django.contrib.admin',
    # ...
]
```

[Django公式ドキュメント](https://docs.djangoproject.com/en/5.1/ref/settings/#installed-apps)によれば、以前の書き方でも動作するようです：

> 各文字列は次のいずれかへの dotted Python パスであるべきです：
> - アプリケーション設定クラス（推奨）
> - アプリケーションモジュール

アプリケーション設定クラスを使用する利点としては、アプリケーションの設定をより細かく制御できることがあります。[StackOverflow](https://stackoverflow.com/questions/60769485/full-config-path-vs-app-name-in-django-installed-apps)で議論されているように、（管理画面などで）より柔軟で制御しやすいそうです。

## 2. F() 式による競合回避

Django のデータベース操作において、競合状態（レースコンディション＝複数の処理が同じデータに同時にアクセスすること）を避けるための `F()` 式が、チュートリアルで使用されるようになりました。以前のチュートリアルでは単純に値を更新する方法が示されていましたが、現在は特に複数のリクエストが同時に処理される可能性がある場合は `F()` 式の使用が推奨されています：

```python
# 昔の方法（競合の可能性あり）
selected_choice.votes += 1
selected_choice.save()

# 現在の方法（F()式を使用）
from django.db.models import F
selected_choice.votes = F('votes') + 1
selected_choice.save()
```

この問題は長い間認識されていました。[Django のチケット #7060](https://code.djangoproject.com/ticket/7060) では、2008年にすでにチュートリアルのレースコンディション問題が報告されていましたが、私の気付かないうちに対応されたようです。

`F()` 式を使用する利点について、[Django 公式ドキュメント](https://docs.djangoproject.com/en/5.1/ref/models/expressions/#avoiding-race-conditions-using-f)では次のように説明しています：

> F() 式は Python でモデルフィールドの値を参照するのではなく、データベース内の値を参照するための Python オブジェクトです。これにより、値を Python メモリに読み込まずに、データベース内のモデルフィールドや注釈付きカラムの値を参照できます。

つまり、`F()` 式を使用すると、Djangoはデータベースレベルで値の更新を行うため、複数のリクエストが同時にデータベースを更新する場合でも競合を避けることができます。これはWebアプリケーションの信頼性向上に貢献する重要な進化です。

例えば、投票アプリケーションにおいて複数のユーザーが同時に投票した場合、F() 式を使わないと以下のような問題が発生する可能性があります：

1. ユーザーA: votes = 5 を読み込む
2. ユーザーB: votes = 5 を読み込む
3. ユーザーA: votes = 5 + 1 = 6 を保存
4. ユーザーB: votes = 5 + 1 = 6 を保存

本来 votes = 7 になるべきところが、votes = 6 のままになってしまいます。F() 式を使用することで、このような競合を防ぐことができます。

## 3. @admin.display デコレータの導入

Django 管理画面で表示されるモデルメソッドのカスタマイズが容易になりました。`@admin.display` デコレータの導入により、クリーンで直感的になりました。

```python
from django.contrib import admin

class Question(models.Model):
    # ...
    
    @admin.display(
        boolean=True,
        ordering='pub_date',
        description='Published recently?',
    )
    def was_published_recently(self):
        now = timezone.now()
        return now - datetime.timedelta(days=1) <= self.pub_date <= now
```

[Django公式ドキュメント](https://docs.djangoproject.com/en/5.1/ref/contrib/admin/#django.contrib.admin.display)によると、`@admin.display` デコレータは Django 3.2 で導入され、次のような点をカスタマイズできます：

- `boolean`: メソッドの戻り値をブール値として表示するかどうか（チェックマークやバツ印で表示）
- `ordering`: 関連するフィールドによる並び替えを可能にする
- `description`: 管理画面に表示される列の見出し

デコレータを使うことで、コードがより宣言的になり、属性を直接設定する場合の潜在的なタイプミスも防げます。特に IDEの自動補完も機能するため、開発体験も向上しています。

[Stack Overflowの投稿](https://stackoverflow.com/questions/77099675/django-admin-display-decorator-sorting-by-multiple-columns)によれば、複数カラムによる並び替えもできるそうで、より柔軟な管理画面のカスタマイズが可能になっています。

## 4. プロジェクトのテンプレートカスタマイズ

プロジェクトレベルでのテンプレートのカスタマイズについて紹介されるようになりました。[Django 公式チュートリアルのパート7](https://docs.djangoproject.com/en/5.1/intro/tutorial07/#customizing-your-project-s-templates)で、管理画面のテンプレートカスタマイズに関するセクションが追加されています。

設定の仕方も、以前は os.path モジュールを使ってパスを組み立てていましたが、現在は Python 3.4 以降で導入された pathlib モジュールを使用しています：

```python
# 昔の方法
import os
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates')],  # os.path.join を使用
        'APP_DIRS': True,
        # ...
    },
]
```

```python
# 現在の方法
from pathlib import Path
BASE_DIR = Path(__file__).resolve().parent.parent

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [BASE_DIR / "templates"],  # パス演算子 / を使用
        'APP_DIRS': True,
        # ...
    },
]
```

pathlib構文を採用することで直感的に理解しやすくなりましたね

## 5. Django Debug Toolbar の紹介

[チュートリアルのパート8](https://docs.djangoproject.com/en/5.1/intro/tutorial08/)が新たに追加され、Django Debug Toolbar が公式に紹介されるようになりました。これは Django エコシステムにおける重要なサードパーティパッケージとして認識されていることを示しています。

公式チュートリアルでは、次のように紹介されています：

> Django Debug Toolbar は Django ウェブアプリケーションをデバッグするための便利なツールです。これはコミュニティ組織 Django Commons によって維持されているサードパーティパッケージです。このツールバーは、アプリケーションがどのように機能するのか理解し、問題を特定するのに役立ちます。

[Django 開発者調査](https://lp.jetbrains.com/django-developer-survey-2023/)によれば、Django Debug Toolbar は最も使用されている上位3つのサードパーティパッケージの一つにランクインしています。

![Screenshot 2025-03-27 at 21.39.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79562/ed64359a-ef1c-4daf-b6b3-5da822f4b95e.png)


Django Debug Toolbarを使用すると、以下のようなデバッグ情報が視覚的に確認できます：

- SQL クエリの実行時間と詳細（どのクエリが実行され、どれだけの時間がかかっているか）
- テンプレートのレンダリング情報（どのテンプレートが使用され、どのようなコンテキスト変数が渡されているか）
- HTTP ヘッダーとリクエスト/レスポンス情報
- キャッシュの利用状況
- シグナルの発火状況
- 実行時間のプロファイリング

さらに、チュートリアルでは「他のサードパーティパッケージのインストール方法」についても説明されるようになり、[Django Packages](https://djangopackages.org/) などのリソースも紹介されています。Djangoの魅力の一つは豊富なサードパーティツールの存在なので、これからDjangoを使ってみようという方はぜひチェックしてみてください。

## まとめ

Django は年々進化を続けており、開発者体験の向上や、より堅牢なアプリケーション開発をサポートする機能が追加されています。チュートリアルにもこのような変更が反映されているようです。

チュートリアルに反映されない部分でも、より安心して使えるフレームワークになるよう、機能の追加やセキュリティ向上が進んでいます。これらの変更を追うことで広範で上質な学びが得られる点もフルスタック・フレームワークであるDjangoの魅力に感じています。

今回チュートリアルに取り組むことで改めて、開発の活発さやDjangoの魅力を感じました。久しぶりに個人プロジェクトなどでDjangoを使っていこうと思います