2026年8月5日、Django 6.1がリリースされました。
公式サイトでのリリース情報は以下を参照してください。
https://docs.djangoproject.com/en/6.1/releases/6.1/
6.1のサポート期限は2027年12月です。5.2 LTSからアップデートするとサポート期限が短くなってしまうことに注意してください(5.2 LTSのサポート期限は2028年4月)。 サポート期限を短くしたくない場合は、2027年4月リリース予定の6.2 LTSまでアップデートしない手もあります(6.2 LTSのサポート期限は2030年4月)。 各バージョンのサポート期限についての詳細は以下公式ドキュメント「Supported Versions」を参照してください。
それでは、主な変更点について紹介します。
Model field fetch modes
モデルに「フェッチモード」という機能が追加されました。 この機能を使うと、未取得のフィールドにアクセスした際にデータベースからデータを取得する方法を制御できます。
フェッチモードの種類は以下のとおりです。
| フェッチモードの種類 | 説明 |
|---|---|
FETCH_ONE(デフォルト) | 現在のインスタンスの不足しているフィールドのみを取得する |
FETCH_PEERS | 同じインスタンスの未取得のフィールドを取得する(明示的にprefetch_related()メソッドを読んだ場合と同じ挙動) |
FETCH_RAISE | 未取得のフィールドにアクセスするとFieldFetchBlocked例外を送出する |
以下のモデルを定義して、Django Debug Toolbarのdebugsqlshellコマンドでどんなクエリが発行されるのか見てみましょう。
from django.db import models
class Author(models.Model):
name = models.CharField(max_length=100)
def __str__(self):
return self.name
class Book(models.Model):
title = models.CharField(max_length=200)
author = models.ForeignKey(Author, on_delete=models.CASCADE)
def __str__(self):
return self.titleFETCH_ONEの場合:
>>> from django.db import models
>>> books = Book.objects.fetch_mode(models.FETCH_ONE)
>>> for book in books:
... print(book.author.name) # authorを呼ぶたびにクエリを投げる(従来通りの挙動)
...
SELECT "books_author"."id",
"books_author"."name"
FROM "books_author"
WHERE "books_author"."id" = 1
LIMIT 21 [0.51ms]
test1
SELECT "books_author"."id",
"books_author"."name"
FROM "books_author"
WHERE "books_author"."id" = 2
LIMIT 21 [0.11ms]
test2
SELECT "books_author"."id",
"books_author"."name"
FROM "books_author"
WHERE "books_author"."id" = 3
LIMIT 21 [0.07ms]
test3FETCH_PEERSの場合:
>>> from django.db import models
>>> books = Book.objects.fetch_mode(models.FETCH_PEERS)
>>> for book in books:
... print(book.author.name) # あらかじめauthorをプリフェッチしてくれる
...
SELECT "books_book"."id",
"books_book"."title",
"books_book"."author_id"
FROM "books_book" [0.12ms]
SELECT "books_author"."id",
"books_author"."name"
FROM "books_author"
WHERE ("books_author"."id") IN ((1), (2), (3)) [0.31ms]
test1
test2
test3FETCH_RAISEの場合:
>>> from django.db import models
>>> books = Book.objects.fetch_mode(models.FETCH_RAISE)
>>> for book in books:
... print(book.author.name) # プリフェッチしていないと例外を投げる
...
SELECT "books_book"."id",
"books_book"."title",
"books_book"."author_id"
FROM "books_book" [0.22ms]
Traceback (most recent call last):
File "<console>", line 2, in <module>
File "/****/site-packages/django/db/models/fields/related_descriptors.py", line 267, in __get__
instance._state.fetch_mode.fetch(self, instance)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^
File "/****/site-packages/django/db/models/fetch_modes.py", line 55, in fetch
raise FieldFetchBlocked(f"Fetching of {klass}.{field_name} blocked.") from None
django.core.exceptions.FieldFetchBlocked: Fetching of Book.author blocked.
>>> for book in books.prefetch_related("author"):
... print(book.author.name) # プリフェッチしていると例外を投げない
...
SELECT "books_book"."id",
"books_book"."title",
"books_book"."author_id"
FROM "books_book" [0.15ms]
SELECT "books_author"."id",
"books_author"."name"
FROM "books_author"
WHERE ("books_author"."id") IN ((1), (2), (3)) [0.28ms]
test1
test2
test3Database-level delete options for ForeignKey.on_delete
ForeignKey.on_deleteにデータベースレベルの削除オプションを指定できるようになりました。今までは、フレームワークから紐づくデータを削除するクエリを投げていましたが、これにより1回のクエリで削除もできるようになりました。
指定できる削除オプションは以下のとおりです。
前述のBook、Authorモデルを使って、どんなクエリが投げられるのか見てみましょう。
まずは、従来通りのCASCADEオプションの場合のクエリを見てみます。
books_book、books_authorテーブルに対してそれぞれDELETE文が投げれられていることが分かります。
>>> Author.objects.filter(id=1).delete()
SELECT "books_author"."id",
"books_author"."name"
FROM "books_author"
WHERE "books_author"."id" = 1 [0.24ms]
BEGIN [0.08ms]
DELETE
FROM "books_book"
WHERE "books_book"."author_id" IN (1) [1.38ms]
DELETE
FROM "books_author"
WHERE "books_author"."id" IN (1) [0.22ms]
(2, {'books.Book': 1, 'books.Author': 1})続いて、モデル定義を以下のように変更してから、もう一度削除処理を実行してみます。
from django.db import models
class Author(models.Model):
name = models.CharField(max_length=100)
def __str__(self):
return self.name
class Book(models.Model):
title = models.CharField(max_length=200)
author = models.ForeignKey(Author, on_delete=models.DB_CASCADE) # ここを変更
def __str__(self):
return self.titleDB_CASCADEの場合は、以下のようにデータベースレベルで紐づくデータを削除するため、DELETE文は1回だけ発行されます。
>>> Author.objects.filter(id=2).delete()
BEGIN [0.25ms]
DELETE
FROM "books_author"
WHERE "books_author"."id" = 2 [0.68ms]
(1, {'books.Author': 1})Mailers
メール送信の設定が変更され、複数の設定を使い分けることができるようになりました。 以下のように、MAILERSに辞書を登録します。
MAILERS = {
"default": { # デフォルトの設定
"BACKEND": "django.core.mail.backends.smtp.EmailBackend",
"OPTIONS": {"host": "smtp.example.com", "use_tls": True},
},
"marketing": { # 目的ごとに別のメール送信設定を書ける
"BACKEND": "example.third.party.EmailBackend",
"OPTIONS": {"region": "africa-1"},
},
}上記の設定を呼び出すには、以下のように書きます。
from django.core.mail import mailers
mailers["marketing"] # marketingの設定を呼び出す
mailers.default # デフォルトの設定を呼び出すまた、従来のメール設定は非推奨になります(Django 2028で削除予定)。 移行方法についての詳細は、以下の公式サイトを参照してください。