Django 5.0主要变更总结

这是Qiita Django Advent Calendar 2023的第11篇文章。

2023年12月4日,Django 5.0发布了。我将解释其中主要的变更。

Django5.0.png

请参考官方网站上的发布信息。

 

Django 5.0 不是长期支持(LTS)版。其支持截止日期为2025年4月。
鉴于支持截止日期早于长期支持版的4.2(截止日期为2026年4月),请慎重考虑是否值得升级至Django 5.0。
有关每个版本的支持截止日期的详细信息,请参阅官方文档中的“Supported Versions”部分。

 

管理员中的面板筛选器

管理员现在可以在筛选器中显示相应的项目数(面板数)。您可以通过ModelAdmin.show_facets属性来进行设置。以下是设置示例。

from django.contrib import admin

from .models import Book

class BookAdmin(admin.ModelAdmin):
    list_display = ("title", "price", "author",)
    list_filter = ("author",)
    show_facets = admin.ShowFacets.ALWAYS  # これを設定

admin.site.register(Book, BookAdmin)

实际的管理员界面会像下面这样显示。

Select_book_to_change___Django_site_admin.jpg

请注意,当显示面数量时,会发出额外的查询,可能会对性能产生影响。

ModelAdmin.show_facets属性可以设置为以下3种类型。

admin.ShowFacets.ALWAYS: 常にファセット数を表示

admin.ShowFacets.ALLOW: ファセット数の表示・非表示を切り替えられる(後述)

admin.ShowFacets.NEVER: ファセット数を表示させない

Select_book_to_change___Django_site_admin.jpg

表单字段渲染的简化模板

创建独自的表单字段模板的写法变得更简单了。
首先,请看一下Django 4.2中的写法。因为需要组合各种方法,所以需要使用相当复杂的写法。

<form>
...
<div>
  {{ form.name.label_tag }}
  {% if form.name.help_text %}
    <div class="helptext" id="{{ form.name.id_for_label }}_helptext">
      {{ form.name.help_text|safe }}
    </div>
  {% endif %}
  {{ form.name.errors }}
  {{ form.name }}
  <div class="row">
    <div class="col">
      {{ form.email.label_tag }}
      {% if form.email.help_text %}
        <div class="helptext" id="{{ form.email.id_for_label }}_helptext">
          {{ form.email.help_text|safe }}
        </div>
      {% endif %}
      {{ form.email.errors }}
      {{ form.email }}
    </div>
    <div class="col">
      {{ form.password.label_tag }}
      {% if form.password.help_text %}
        <div class="helptext" id="{{ form.password.id_for_label }}_helptext">
          {{ form.password.help_text|safe }}
        </div>
      {% endif %}
      {{ form.password.errors }}
      {{ form.password }}
    </div>
  </div>
</div>
...
</form>

在Django 5.0中,您可以使用as_field_group()方法将其简化为以下编写方式,这与之前提到的模板具有相同的意义。

<form>
...
<div>
  {{ form.name.as_field_group }}
  <div class="row">
    <div class="col">{{ form.email.as_field_group }}</div>
    <div class="col">{{ form.password.as_field_group }}</div>
  </div>
</div>
...
</form>

数据库计算的默认值

新增了一个Field.db_default属性,用于设置默认值。与传统的Field.default属性不同的是,您可以在数据库端计算并设置值。

以下是代码示例。

from django.db import models
from django.db.models.functions import Now, Pi

class Example(models.Model):
    name = models.CharField(max_length=100)
    created_at = models.DateTimeField(db_default=Now())
    circumference = models.FloatField(db_default=2 * Pi())

使用上述模型进行数据注册,结果如下所示。

>>> from example.models import Example
>>> example = Example.objects.create(name="example")
>>> example.created_at
datetime.datetime(2023, 11, 14, 14, 52, 50, 579000, tzinfo=datetime.timezone.utc)
>>> example.circumference
6.283185307179586

然而,Field.db_default属性无法指定引用其他字段的值。

from django.db import models
from django.db.models import F
from django.db.models import Value as V
from django.db.models.functions import Concat

class Example(models.Model):
    first_name = models.CharField(max_length=100)
    last_name = models.CharField(max_length=100)
    full_name = models.TextField(
        # NG
        db_default=Concat(F("first_name"), V(" "), F("last_name")),
    )

当在上述的模型定义的状态下执行迁移命令时,会出现以下错误。

$ python manage.py migrate
SystemCheckError: System check identified some issues:

ERRORS:
example.Example.full_name: (fields.E012) Concat(ConcatPair(F(first_name), ConcatPair(Value(' '), F(last_name)))) cannot be used in db_default.

数据库生成的模型字段

在数据库中生成的值被处理时,增加了GeneratedField。
在以下示例中,我们使用GeneratedField将first_name和last_name引用的值生成为full_name字段。

from django.db import models
from django.db.models import F
from django.db.models import Value as V
from django.db.models.functions import Concat

class Example(models.Model):
    first_name = models.CharField(max_length=100)
    last_name = models.CharField(max_length=100)
    full_name = models.GeneratedField(
        expression=Concat(F("first_name"), V(" "), F("last_name")),
        db_persist=True,  # TrueかFalseを必ず指定する
    )

当db_persist为True时,将值存储到存储设备的设置。必须指定为True或False。可以指定哪个取决于使用的数据库不同。

让我们使用上述模型来注册数据。注册后,您可以看到full_name已经被生成。而且,如果在注册后更新了first_name和last_name,full_name也会被更新。

>>> from example.models import Example
>>> example = Example.objects.create(first_name="Taro", last_name="Yamada")
>>> example.full_name  # first_nameとlast_nameの登録内容が反映される
'Taro Yamada'
>>> Example.objects.filter(pk=example.pk).update(first_name="Hanako", last_name="Suzuki")
1
>>> example.refresh_from_db()
>>> example.full_name  # first_nameとlast_nameの変更内容が反映される
'Hanako Suzuki'

让我们来查看表定义(本文中使用的是 SQLite 3.39.5)。

$ python manage.py sqlmigrate example 0001
BEGIN;
--
-- Create model Example
--
CREATE TABLE "example_example" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "first_name" varchar(100) NOT NULL, "last_name" varchar(100) NOT NULL, "full_name" varchar(100) GENERATED ALWAYS AS (COALESCE("first_name", '') || COALESCE(COALESCE(' ', '') || COALESCE("last_name", ''), '')) STORED);
COMMIT;

full_name的定义是”full_name” varchar(100),使用数据库功能生成值,生成规则为(COALESCE(“first_name”, ”) || COALESCE(COALESCE(‘ ‘, ”) || COALESCE(“last_name”, ”), ”)) STORED。

提供更多领域选择的选项

Field.choices属性和ChoiceField.choices属性可以指定的值种类增加了两个。

现在,您可以直接指定枚举类型而不使用枚举类型的.choices属性来列举枚举类型。

首先,请看一下Django 4.2之前的枚举类型的使用方法。

from django.db import models

class Post(models.Model):
    class Status(models.TextChoices):
        PUBLISHED = "published", "公開"
        DRAFT = "draft", "下書き"

    status = models.CharField(
        max_length=20,
        choices=Status.choices,  # choices属性を指定する
        default=Status.DRAFT,
    )

从Django 5.0开始,可以按以下方式编写。

from django.db import models

class Post(models.Model):
    class Status(models.TextChoices):
        PUBLISHED = "published", "公開"
        DRAFT = "draft", "下書き"

    status = models.CharField(
        max_length=20,
        choices=Status,  # choices属性を省略して直接列挙型を指定できる
        default=Status.DRAFT,
    )

第二个选项是,您可以传递以下可以调用的对象。

from django.db import models

def get_statuses():
    return {
        "published": "公開",
        "draft": "下書き",
    }

class Post(models.Model):
    status = models.CharField(
        max_length=20,
        choices=get_statuses,
        default="draft",
    )
请参考以下网址:https://docs.djangoproject.com/en/5.0/ref/contrib/admin/#django.contrib.admin.ModelAdmin.ShowFacets
广告
将在 10 秒后关闭
bannerAds