こんにちは、PyQサポートです。
2023年12月4日に Django 5.0 がリリースされました。
この記事では、Django 5.0のリリースノート から、次の項目について紹介します。
Django 5.0の主な新機能(カッコ内は意訳です)
- Facet filters in the admin (管理画面のフィルターに件数を表示)
- Simplified templates for form field rendering (テンプレートでのフォームフィールドの記述を簡素化)
- Database-computed default values (データベースレベルでのデフォルト制約)
- Database generated model field (生成列を定義できるモデルフィールドの追加)
- More options for declaring field choices (より書きやすくなった選択項目の記述方法)
Django 5.0 リリースの概要
名称 | Django |
---|---|
リリースバージョン | Django5.0 |
リリース日 | 2023年12月4日 |
サポートしているPython | 3.10, 3.11, 3.12 |
サポートしているPython | https://docs.djangoproject.com/en/5.0/releases/5.0/ |
※アップデートの際はBackwards Incompatible Changes(後方互換性のない変更)に十分注意してください
Django 5.0 で追加された新機能
Facet filters in the admin
管理画面のフィルターに、絞り込みした件数が表示されるようになりました。
この件数の表示は、以下のようにshow_facets
オプションで「常に表示(ALWAYS
)」「表示・非表示の切り替え可(ALLOW
)」「常に非表示(NEVER
)」のいずれかを設定できます。デフォルトはALLOW
です。
from django.contrib import admin class MyModelAdmin(admin.ModelAdmin): ... # Have facets always shown for this model admin. show_facets = admin.ShowFacets.ALWAYS
絞り込み件数が表示されることで、管理画面がより便利になりますね。
Simplified templates for form field rendering
DjangoのForm
クラスを使ってテンプレートに入力フィールドを個別に記述する際、as_field_group
を使ってシンプルに書けるようになりました。
以下のNameForm
を例に説明します。
from django import forms class NameForm(forms.Form): name = forms.CharField( label="Your name", max_length=100, help_text="100 characters max.", )
name
の入力フィールドを表示する場合、通常はラベルやヘルプ文言なども合わせて表示するでしょう。そのため、テンプレートでは以下のように書く必要があります。
<form> ... <div> {{ form.name.label_tag }} {% if form.name.help_text %} <div class="helptext" id="{{ form.name.auto_id }}_helptext"> {{ form.name.help_text|safe }} </div> {% endif %} {{ form.name.errors }} {{ form.name }} </div> ... </form>
Django 5.0から追加されたas_field_group
を使うことで、上記の内容を以下のように書けます。
<form> ... <div> {{ form.name.as_field_group }} </div> ... </form>
とてもシンプルになりましたね。
なお、as_field_group
でのレンダリングに使われるテンプレートはデフォルトではdjango/forms/field.html
ですが、これはカスタマイズ可能です。これまでは特定のフィールドをカスタマイズ表示したい場合、書かないといけない行数が多く面倒でしたが、 as_field_group
を使えばシンプルに記述できそうですね。
Database-computed default values
モデルフィールドのオプションにdb_default
が追加され、データベースレベルでデフォルト値を設定できるようになりました。
もともとデフォルト値を設定するオプションとしてdefault
がありましたが、こちらはPythonで処理されるものであり、データベースレベルでのデフォルト値ではありませんでした。
比較のため、以下のようなMyModel
(db_default
を使用)とMyModel2
(default
を使用)を定義し、マイグレーションファイルを作成します。そしてsqlmigrate
コマンドでDDLを確認してみましょう。
from django.db import models from django.db.models.functions import Now, Pi from django.utils import timezone class MyModel(models.Model): age = models.IntegerField(db_default=18) created = models.DateTimeField(db_default=Now()) circumference = models.FloatField(db_default=2 * Pi()) def circumference_default(): return 2 * math.pi class MyModel2(models.Model): age = models.IntegerField(default=18) created = models.DateTimeField(default=timezone.now) circumference = models.FloatField(default=circumference_default)
マイグレーションファイルから生成されるDDLは以下です(データベースはSQLite3)。
見やすさのため整形しています。
BEGIN; -- -- Create model MyModel -- CREATE TABLE "sample_mymodel"( "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "age" integer DEFAULT 18 NOT NULL, "created" datetime DEFAULT(STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')) NOT NULL, "circumference" real DEFAULT((2 * PI())) NOT NULL ); -- -- Create model MyModel2 -- CREATE TABLE "sample_mymodel2"( "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "age" integer NOT NULL, "created" datetime NOT NULL, "circumference" real NOT NULL ); COMMIT;
db_default
を使用したMyModel
は、DDLにデフォルト制約(DEFAULT
)が付与されることが確認できました。
データベースレベルでデフォルト値が設定できるようになったことで、より強固なデフォルト制約をかけられます。
Database generated model field
モデルフィールドに新しくGeneratedField
が追加され、Djangoのモデルで「生成列」を定義できるようになりました。生成列は、常に他のカラムから計算される列です。
たとえば以下のarea
フィールドは、side
フィールドの値から計算されます。
from django.db import models from django.db.models import F class Square(models.Model): side = models.IntegerField() area = models.GeneratedField( expression=F("side") * F("side"), output_field=models.BigIntegerField(), db_persist=True, )
こちらも、マイグレーションファイル作成後、sqlmigrate
コマンドでDDLを確認してみます。
BEGIN; -- -- Create model Square -- CREATE TABLE "sample_square"( "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "side" integer NOT NULL, "area" bigint GENERATED ALWAYS AS(("side" * "side")) STORED ); COMMIT;
GENERATED ALWAYS AS
により、生成列が定義されていることが確認できました。
これまでも、以下のように@property
を使って似たようなことはできました。
class Square2(models.Model): side = models.IntegerField() @property def area(self): return self.side ** 2
しかし、@property
で定義した項目はクエリセットでは利用できません。
# エラーになる Square2.objects.filter(area__gt=10)
Django 5.0 で追加されたGeneratedField
を使って定義したフィールドであれば、クエリセットでも利用できます。
# エラーにならない Square.objects.filter(area__gt=10)
More options for declaring field choices
モデルフィールドのchoices
オプションと、フォームフィールド ChoiceField
のchoices
オプションの設定が、より書きやすくなりました。
具体例として、以下の2つのモデルを比較してみましょう。
まず、以下はDjango 4.2までの書き方です。
from django.db import models Medal = models.TextChoices("Medal", "GOLD SILVER BRONZE") SPORT_CHOICES = [ ("Martial Arts", [("judo", "Judo"), ("karate", "Karate")]), ("Racket", [("badminton", "Badminton"), ("tennis", "Tennis")]), ("unknown", "Unknown"), ] class Winner(models.Model): name = models.CharField(...) medal = models.CharField(..., choices=Medal.choices) sport = models.CharField(..., choices=SPORT_CHOICES)
次に、以下はDjango 5.0での書き方です。
from django.db import models Medal = models.TextChoices("Medal", "GOLD SILVER BRONZE") SPORT_CHOICES = { # Using a mapping instead of a list of 2-tuples. "Martial Arts": {"judo": "Judo", "karate": "Karate"}, "Racket": {"badminton": "Badminton", "tennis": "Tennis"}, "unknown": "Unknown", } def get_scores(): return [(i, str(i)) for i in range(10)] class Winner(models.Model): name = models.CharField(...) medal = models.CharField(..., choices=Medal) # Using `.choices` not required. sport = models.CharField(..., choices=SPORT_CHOICES) score = models.IntegerField(choices=get_scores) # A callable is allowed.
1点目は、列挙型であるMedal
をchoices
に指定する際、Medal.choices
ではなくMedal
と書けるようになりました。
2点目は、choices
に設定する「値と表示名の組み合わせ」を、タプルのリストではなく辞書で書けるようになりました。SPORT_CHOICES
を比較してみてください。辞書のほうがより分かりやすいと思います。
3点目は、choices
に呼び出し可能オブジェクトを指定できるようになりました。上記の例ではget_scores
を指定しています。これを使うことで、choices
の設定値を動的に生成できます。
まとめ
Django 5.0 の主な新機能について紹介しました。
特にデータベース関連の新機能は、今後の開発で活用したいと思いました。
これからDjangoで新規開発するのであれば、まずは Django 5.0 の使用を検討するとよいと思います。
なお、Django 5.0 には他にも多くの変更がありますので、詳しくは Django 5.0のリリースノート をご確認ください。