この記事は、さくらインターネット Advent Calendar 2025 13日目の記事です。

2025年12月3日、Django 6.0がリリースされました。

公式サイトでのリリース情報は以下を参照してください。

https://docs.djangoproject.com/en/6.0/releases/6.0/

6.0のサポート期限は2027年4月です。5.2 LTSからアップデートするとサポート期限が短くなってしまうことに注意してください(5.2 LTSのサポート期限は2028年4月)。 サポート期限を短くしたくない場合は、2027年4月リリース予定の6.2 LTSまでアップデートしない手もあります(6.2 LTSのサポート期限は2030年4月)。 各バージョンのサポート期限についての詳細は以下公式ドキュメント「Supported Versions」を参照してください。

Download Django | Django

それでは、主な変更点について紹介します。


Content Security Policy support

コンテンツセキュリティポリシー(CSP)をサポートし、サイトが読み込めるコンテンツを制御できるようになりました。 これにより、さらに強力なクロスサイトスクリプティング対策を取れるようになりました。

CSPを有効にするには、ContentSecurityPolicyMiddlewareMIDDLEWAREに追加します。

MIDDLEWARE = [
    # (省略)
    "django.middleware.csp.ContentSecurityPolicyMiddleware",
    # (省略)
]

次に、settings.pyに以下の設定を加えます。

from django.utils.csp import CSP

SECURE_CSP = {
    "default-src": [CSP.SELF],
}

SECURE_CSP_REPORT_ONLY = {
    "default-src": [CSP.SELF],
    "report-uri": "/path/to/reports-endpoint/",
}

runserverコマンドでアプリケーションを起動し、curlコマンドでレスポンスヘッダーの内容を見てみましょう。 Content-Security-PolicyContent-Security-Policy-Report-Onlyが設定されていることがわかります。

$ curl -I http://127.0.0.1:8000/
HTTP/1.1 200 OK
Date: Mon, 08 Dec 2025 04:55:43 GMT
Server: WSGIServer/0.2 CPython/3.14.2
Content-Type: text/html; charset=utf-8
Content-Security-Policy: default-src 'self'
Content-Security-Policy-Report-Only: default-src 'self'; report-uri /path/to/reports-endpoint/
X-Frame-Options: DENY
Content-Length: 16
X-Content-Type-Options: nosniff
Referrer-Policy: same-origin
Cross-Origin-Opener-Policy: same-origin

Template Partials

Template Partialsは、テンプレートの一部分を部品化して使い回せるようにできる機能です。

Template Partialsの使い方について説明します。 まず、以下のように、テンプレートの部品化したい部分をpartialdefタグで囲います。

user-infoはプログラマーが任意につけられるTemplate Partialsの名前です。用途がわかる名前をつけるようにしてください。 Template Partialsを呼び出すタグはpartialです。

{# user-infoというTemplate Partialsを定義 #}
{% partialdef user-info %}
<div>
    {% if user.is_authenticated %}{{ user.get_username }}{% else %}ゲスト{% endif %}さん、こんにちは。
</div>
{% endpartialdef %}
{# user-infoを使いたい部分にこれを書く #}
{% partial user-info %}

上記のテンプレートをブラウザで表示すると、以下の内容が出力されます(あらかじめユーザーを作ってログインしています)。

Template Partialの表示内容
Template Partialの表示内容

partialdefタグで囲った部分は描画されません。 上記の例は、{% partial user-info %}のみが出力されます。 描画したい場合は、inlineというパラメーターを渡します。 以下の例は、{% partialdef user-info inline %}...{% endpartialdef %}{% partial user-info %}両方が出力されます。

{% partialdef user-info inline %}  {# ここを変更 #}
<div>
    {% if user.is_authenticated %}{{ user.get_username }}{% else %}ゲスト{% endif %}さん、こんにちは。
</div>
{% endpartialdef %}

{% partial user-info %}

上記のテンプレートをブラウザで表示すると、以下の内容が出力されます。

inlineパラメーターを使用した場合の表示内容
inlineパラメーターを使用した場合の表示内容

さらに、partialdefで囲った部分だけを抜き出して描画するビューを書くこともできます。

以下のようにpartialdefタグを書いたテンプレートファイルのパスの後ろ#をつけて、Template Partialsの名前を指定すると、Template Partialsの部分だけを抜き出すことができます。辞書型でコンテキスト変数を渡すこともできます。

""""example/views.py"""
from django.contrib.auth.models import User
from django.shortcuts import get_object_or_404, render

def user_info_partial(request, user_id):
    user = get_object_or_404(User, id=user_id)
    return render(
        request,
        # テンプレートファイルのパスの後ろに「#」をつけてTemplate Partialsの名前を指定
        "example/index.html#user-info",
        # コンテキスト変数userを渡している
        {"user": user},
    )

これは、htmxのようなJavaScriptライブラリから呼び出して、動的なコンテンツを出力する場合に役立つ機能です。

説明のために、簡単なアプリケーションを作ってみます。exampleというDjangoアプリケーションを作成し、以下のコードを書きます。

""""example/views.py"""
from django.views.generic import TemplateView
from django.contrib.auth.models import User
from django.shortcuts import get_object_or_404, render

class IndexView(TemplateView):
    """user-infoというTemplate Partialsを定義したトップページ"""
    template_name = "example/index.html"

def user_info_partial(request, user_id):
    """トップページのuser-infoだけ抜き出すビュー"""
    user = get_object_or_404(User, id=user_id)
    return render(request, "example/index.html#user-info", {"user": user})

class HtmxView(TemplateView):
    """htmxを使って動的にコンテキストを読み込むページ"""
    template_name = "example/htmx.html"
"""example/urls.py"""
from django.urls import path
from . import views

app_name = "example"

urlpatterns = [
    path("", views.IndexView.as_view(), name="index"),
    path("user-info/<int:user_id>/", views.user_info_partial, name="user_info_partial"),
    path("htmx/", views.HtmxView.as_view(), name="htmx"),
]
{# example/templates/example/index.html #}
{% partialdef user-info %}
<div>
    {% if user.is_authenticated %}{{ user.get_username }}{% else %}ゲスト{% endif %}さん、こんにちは。
</div>
{% endpartialdef %}

{% partial user-info %}
{# example/templates/example/htmx.html #}
<html>
    <head>
        <title>HTMX Example</title>
        <script src="https://cdn.jsdelivr.net/npm/htmx.org@2.0.8/dist/htmx.min.js" integrity="sha384-/TgkGk7p307TH7EXJDuUlgG3Ce1UVolAOFopFekQkkXihi5u/6OCvVKyz1W+idaz" crossorigin="anonymous"></script>
    </head>
    <body>
        <h1>HTMX Example</h1>
        <button hx-get="{% url 'example:user_info_partial' user_id=1 %}" hx-target="#user-info" hx-swap="innerHTML">
            ユーザー情報を取得
        </button>
        <div id="user-info"></div>
</html>

なお、「Content Security Policy support」で紹介したContentSecurityPolicyMiddlewareMIDDLEWAREに入れている場合はhtmxのCDNを読み込めないので、アプリケーションを立ち上げる前にMIDDLEWARE内のContentSecurityPolicyMiddlewareは外してください。 このアプリケーションを起動すると、以下のような画面を動かすことができます。

画面の初期状態
画面の初期状態
ボタンクリック後の状態
ボタンクリック後の状態

Background Tasks

ついに、Djangoで非同期タスクの実行を標準サポートするようになりました。 今まではCeleryなどのサードパーティライブラリと組み合わせるのが定番でしたが、今後はDjangoのみでこの機能を実現できます。

非同期タスクを定義するには、関数にtaskデコレーターをつけます。

from django.tasks import task

from books.models import Book

@task
def delete_books(ids=()):
    """書籍をまとめて削除する"""
    return Book.objects.filter(id__in=ids).delete()

taskデコレーターをつけた関数からenqueue()というメソッドを呼び出せます。 このメソッドに関数に定義したのと同じ引数を渡せるようになっています。

>>> delete_books.enqueue(ids=[1, 2, 3])
Task id=xqNMTYSveNYTuWgj82NU36W6uY2NAnqA path=books.tasks.delete_books state=RUNNING
Task id=xqNMTYSveNYTuWgj82NU36W6uY2NAnqA path=books.tasks.delete_books state=SUCCESSFUL
NoneType: None
TaskResult(task=Task(priority=0, func=<function delete_books at 0x1094f9900>, backend='default', queue_name='default', run_after=None, takes_context=False), id='xqNMTYSveNYTuWgj82NU36W6uY2NAnqA', status=TaskResultStatus.SUCCESSFUL, enqueued_at=datetime.datetime(2025, 12, 9, 12, 59, 1, 163865, tzinfo=datetime.timezone.utc), started_at=datetime.datetime(2025, 12, 9, 12, 59, 1, 165274, tzinfo=datetime.timezone.utc), finished_at=datetime.datetime(2025, 12, 9, 12, 59, 1, 177662, tzinfo=datetime.timezone.utc), last_attempted_at=datetime.datetime(2025, 12, 9, 12, 59, 1, 165274, tzinfo=datetime.timezone.utc), args=[], kwargs={'ids': [1, 2, 3]}, backend='default', errors=[], worker_ids=['KcrO2fgLXCXKudU31kf43OWSGBjaBhFh'], _return_value=[3, {'books.Book': 3}])

戻り値はTaskResultです。 このオブジェクトを通して、タスクの進捗状況を確認できます。

非同期タスクのバックエンドの情報はTASKSで設定できます。

Django組み込みのバックエンドは以下の2つのみで、どちらも開発やテストのためのものです。

本番で使うにはBaseTaskBackendを継承してRedisやRabbitMQと通信する実装が必要です。 私が調べた限り、現時点では該当するサードパーティライブラリは見当たりませんでした。

また、Celeryなどで提供している、ジョブの定期実行機能はありません。 定期実行が必要な場合は、cronジョブで代用するか、従来通りCeleryなどのサードパーティライブラリを利用するしかなさそうです。