Django 6.0来袭!这些新特性,真的令人振奋!

原文链接:adamj.eu/tech/2025/1...

作者:Adam Johnson

译者:倔强青铜三

前言

大家好,我是倔强青铜三 。欢迎关注我,微信公众号:倔强青铜三。欢迎点赞、收藏、关注,一键三连!!!

Django 6.0 已于 2025 年 12 月 3 日发布,为这个已经 20 岁的 Python Web 框架开启了新的发布周期。该版本带来了众多新特性,以下是其中的一些亮点。

使用 django-upgrade 升级项目

如果你正在从 Django 5.2 或更早版本升级项目,可以尝试使用我的工具 django-upgrade。它会自动更新旧的 Django 代码以使用新特性,修复一些弃用警告,包括针对 Django 6.0 的五个修复程序。

模板局部定义(Template Partials)

Django 模板语言现在支持模板局部定义(template partials),这使得在模板文件中封装和重用小型命名片段变得更加容易。局部定义由新的 {% partialdef %}{% endpartialdef %} 标签标记,可以在同一模板中重复使用,也可以单独渲染。

在同一模板中重复使用局部定义

以下模板在同一个模板中重复使用了一个名为 filter_controls 的局部定义。它在模板顶部定义一次,然后在后面两次使用。使用局部定义可以让模板避免重复,而无需将内容推送到单独的包含文件中。

html 复制代码
<section id="videos">
  {% partialdef filter_controls %}
    <form>
      {{ filter_form }}
    </form>
  {% endpartialdef %}

  {% partial filter_controls %}

  <ul>
    {% for video in videos %}
      <li>
        <h2>{{ video.title }}</h2>
        ...
      </li>
    {% endfor %}
  </ul>

  {% partial filter_controls %}
</section>

实际上,我们可以通过在 partialdef 标签上使用 inline 选项,进一步简化这种模式,这会导致定义在原地渲染。

html 复制代码
<section id="videos">
  {% partialdef filter_controls inline %}
    <form>
      {{ filter_form }}
    </form>
  {% endpartialdef %}

  <ul>
    {% for video in videos %}
      <li>
        <h2>{{ video.title }}</h2>
        ...
      </li>
    {% endfor %}
  </ul>

  {% partial filter_controls %}
</section>

当你发现自己在同一个模板中重复模板代码时,可以使用这种模式。因为局部定义可以使用变量,你也可以使用它们来消除重复,当渲染具有不同数据的类似控件时。

单独渲染局部定义

以下模板定义了一个 view_count 局部定义,它旨在单独重新渲染。它使用了 inline 选项,因此当整个模板被渲染时,局部定义会被包含。

页面使用了 htmx,通过我的 django-htmx 包,通过 hx-* 属性定期刷新查看次数。来自 htmx 的请求会发送到一个专用视图,该视图重新渲染 view_count 局部定义。

html 复制代码
{% load django_htmx %}
<!doctype html>
<html>
  <body>
    <h1>{{ video.title }}</h1>
    <video width="1280" height="720" controls>
      <source src="{{ video.file.url }}" type="video/mp4">
      Your browser does not support the video tag.
    </video>

    {% partialdef view_count inline %}
    <section
      class="view-count"
      hx-trigger="every 1s"
      hx-swap="outerHTML"
      hx-get="{% url 'video-view-count' video.id %}"
    >
      {{ video.view_count }} views
    </section>
    {% endpartialdef %}

    {% htmx_script %}
  </body>
</html>

两个视图的相关代码可能如下所示:

python 复制代码
from django.shortcuts import render

def video(request, video_id):
    ...
    return render(request, "video.html", {"video": video})

def video_view_count(request, video_id):
    ...
    return render(request, "video.html#view_count", {"video": video})

初始的 video 视图渲染了完整的模板 video.htmlvideo_view_count 视图通过在模板名称后附加 #view_count 来仅渲染 view_count 局部定义。这种语法类似于在 URL 中通过其 ID 引用 HTML 片段的方式。

任务框架(Tasks Framework)

Django 现在包含了一个内置的任务框架,用于在 HTTP 请求 - 响应周期之外运行代码。这使得可以将工作(如发送电子邮件或处理数据)卸载到后台工作程序中。

本质上,这是一个用于定义和排队后台任务的新 API------非常酷!

后台任务是一种在请求 - 响应周期之外运行代码的方式。它们是 Web 应用程序中的常见需求,用于发送电子邮件、处理图像、生成报告等。

历史上,Django 没有提供任何后台任务系统,而是完全忽略了这个问题。开发人员依赖于第三方包,如 CeleryDjango Q2。尽管这些系统很好,但它们设置和维护起来可能很复杂,而且通常不符合 Django 的"自然"风格。

新的任务框架通过提供一个定义后台任务的接口来填补这一空白,任务运行器包可以与之集成。这个共同的基础允许第三方 Django 包以标准方式定义任务,假设你会使用兼容的任务运行器来执行它们。

使用新的 @task 装饰器定义任务:

python 复制代码
from django.tasks import task

@task
def resize_video(video_id):
    ...

然后使用 Task.enqueue() 方法将它们排队进行后台执行:

python 复制代码
from example.tasks import resize_video

def upload_video(request):
    ...
    resize_video.enqueue(video.id)
    ...

执行任务

目前,Django 并没有包含一个生产就绪的任务后端,只有两个适用于开发和测试的后端:

  • ImmediateBackend:同步运行任务,直到它们完成才会阻塞。
  • DummyBackend:当任务被排队时什么都不做,但允许之后检查任务。在测试中很有用,你可以断言任务被排队了,而无需实际运行它们。

对于生产使用,你需要使用一个第三方包,其中 django-tasks 是主要的选择。它提供了 DatabaseBackend,用于将任务存储在你的 SQL 数据库中,对于许多项目来说是一个很好的解决方案,避免了额外的基础设施,并允许在数据库事务中原子性地排队任务。我们可能会在适当的时候看到这个后端合并到 Django 中,或者至少成为一个官方包,以帮助使 Django 在后台任务方面"开箱即用"。

要使用 django-tasks 的 DatabaseBackend,首先安装该包:

其次,将这两个应用添加到你的 INSTALLED_APPS 设置中:

python 复制代码
INSTALLED_APPS = [
    # ...
    "django_tasks",
    "django_tasks.backends.database",
    # ...
]

第三,将 DatabaseBackend 配置为你的任务后端,使用新的 TASKS 设置:

python 复制代码
TASKS = {
    "default": {
        "BACKEND": "django_tasks.backends.database.DatabaseBackend",
    },
}

第四,运行迁移以创建必要的数据库表。

最后,要运行任务工作进程,使用包的 db_worker 管理命令:

bash 复制代码
$ ./manage.py db_worker
Starting worker worker_id=jWLMLrms3C2NcUODYeatsqCFvd5rK6DM queues=default

该进程会无限期运行,轮询任务并执行它们,同时记录事件:

bash 复制代码
Task id=10b794ed-9b64-4eed-950c-fcc92cd6784b path=example.tasks.echo state=RUNNING
Hello from test task!
Task id=10b794ed-9b64-4eed-950c-fcc92cd6784b path=example.tasks.echo state=SUCCEEDED

你希望在生产环境中运行 db_worker,并且如果你希望在开发中测试后台任务执行,也应该运行它。

内容安全策略(CSP)支持

Django 现在提供了对 内容安全策略(CSP) 标准的内置支持,这使得保护 Web 应用程序免受内容注入攻击(如跨站脚本攻击(XSS))变得更加容易。CSP 允许通过为浏览器提供严格的规则来声明哪些脚本、样式、图像或其他资源可以被加载,从而声明可信的内容来源。

我对此感到非常兴奋,因为我是一个安全爱好者,多年来一直在为客户项目部署 CSP。

CSP 是一种安全标准,可以保护你的网站免受跨站脚本攻击(XSS)和其他代码注入攻击。你设置一个 content-security-policy 标头来声明哪些内容来源对你的网站是可信的,然后浏览器会阻止来自其他来源的内容。例如,你可能会声明只有你域名下的脚本才是允许的,因此如果攻击者设法注入一个指向 evil.com<script> 标签,浏览器会阻止加载它,因为该来源未被信任。

以前,Django 没有内置的 CSP 支持,开发人员不得不依赖于自己构建,或者使用像非常流行的 django-csp 这样的第三方包。但这有点不方便,因为它意味着其他第三方包无法可靠地与 CSP 集成,因为没有通用的 API 可以使用。

新的 CSP 支持提供了 django-csp 所有的核心功能,并且具有稍微整洁一些且更符合 Django 风格的 API。要开始使用,首先ContentSecurityPolicyMiddleware 添加到你的 MIDDLEWARE 设置中:

python 复制代码
MIDDLEWARE = [
    # ...
    "django.middleware.csp.ContentSecurityPolicyMiddleware",
    # ...
]

将其放置在 SecurityMiddleware 附近,因为它类似于为所有响应添加安全相关标头。(你确实启用了 SecurityMiddleware,对吧?)

其次,使用新的设置配置你的 CSP 策略:

  • SECURE_CSP:配置 content-security-policy 标头,这是你实际执行的策略。
  • SECURE_CSP_REPORT_ONLY:配置 content-security-policy-report-only 标头,这设置了一个非强制策略,浏览器会向指定的端点报告违反策略的情况。这个选项对于在强制执行策略之前测试和监控策略非常有用。

例如,要采用 web.dev 推荐的基于 nonce 的严格 CSP,你可以从以下设置开始:

python 复制代码
from django.utils.csp import CSP

SECURE_CSP_REPORT_ONLY = {
    "script-src": [CSP.NONCE, CSP.STRICT_DYNAMIC],
    "object-src": [CSP.NONE],
    "base-uri": [CSP.NONE],
}

上面使用的 CSP 枚举提供了 CSP 指令的常量,以帮助避免拼写错误。

这个策略非常严格,如果直接部署,会破坏大多数现有网站,因为它需要使用 nonce,接下来会介绍。这就是为什么示例中显示从报告模式标头开始,以帮助在强制执行策略之前跟踪需要修复的地方。你之后会将 SECURE_CSP 设置改为强制执行策略。

总之,这些就是设置新的 CSP 支持的两个基本步骤!

随机数生成(Nonce Generation)

新特性的关键部分之一是,当使用 CSP 中间件时,Django 现在内置了 随机数生成 功能。随机数是 CSP 中的一种安全特性,允许你通过在 <script><style> 标签上使用 nonce 属性来标记特定的标签为可信的:

html 复制代码
<script src="/static/app.js" type="module" nonce="55vsH4w7ATHB85C3MbPr_g"></script>

随机数值是按请求随机生成的,并包含在 CSP 标头中。执行内容注入的攻击者无法猜测随机数,因此浏览器可以只信任那些包含正确随机数的标签。因为随机数生成现在是 Django 的一部分,第三方包可以依赖它为它们的 <script><style> 标签提供随机数,如果你采用带有随机数的 CSP,它们将继续工作。

随机数是当今使用 CSP 的推荐方式,避免了以前基于允许列表的方法所带来的问题。这就是为什么上面推荐的策略启用了随机数。要采用基于随机数的策略,你需要通过以下步骤为 <script><style> 标签添加随机数值。

首先 ,将新的 csp 模板上下文处理器添加到你的 TEMPLATES 设置中:

python 复制代码
TEMPLATES = [
    {
        "BACKEND": "django.template.backends.django.DjangoTemplates",
        "OPTIONS": {
            "context_processors": [
                # ...
                "django.template.context_processors.csp",
            ],
        },
    },
]

其次 ,使用 nonce="{{ csp_nonce }}"<script><style> 标签添加注释:

html 复制代码
-   <script src="{% static 'app.js' %}" type="module"></script>
+   <script src="{% static 'app.js' %}" type="module" nonce="{{ csp_nonce }}"></script>

这可能会很繁琐且容易出错,因此首先使用报告模式来监控违规情况可能会很有用,尤其是在大型项目中。

总之,正确部署 CSP 可能会成为另一篇博文的主题,甚至是一本书的章节,所以我们就先说到这里。更多信息,请查看 web.dev 文章MDN CSP 指南

邮件 API 更新

Django 中的邮件处理现在使用了 Python 的现代邮件 API,该 API 在 Python 3.6 中引入。这个以 email.message.EmailMessage 类为中心的 API,为撰写和发送邮件提供了一个更简洁且支持 Unicode 的接口。

这是一个重大变化,但如果你的项目只使用基本的邮件功能,可能不会受到影响。你仍然可以像以前一样使用 Django 的 send_mail() 函数和 EmailMessage 类,例如:

python 复制代码
from django.core.mail import EmailMessage

email = EmailMessage(
    subject="🐼 需要更多竹子",
    body="我们的竹子快没了,请在熊猫发现之前补货!",
    from_email="zookeeper@example.com",
    to=["supplies@example.com"],
)
email.attach_file("/media/bamboo_cupboard.jpg")
email.send()

关键的变化是,在你调用 Django EmailMessage 对象的 send() 方法时,它现在会在发送之前将自己转换为 Python 的新 email.message.EmailMessage 类型。

现代化带来了以下好处:

  1. 更少的错误:Python 旧邮件 API 中的许多边缘情况错误已在新 API 中修复。
  2. Django 更简洁:Django 邮件代码中的一系列变通方法和安全修复已被移除。
  3. 更方便的 API:新 API 支持一些便利功能,例如下面的内联附件示例。

使用 MIMEPart 更容易地添加内联附件

Django 的 EmailMessage.attach() 方法允许你将文件作为附件添加。电子邮件支持图像作为 内联附件,这些图像可以显示在 HTML 邮件正文中。

虽然你以前可以使用 EmailMessage.attach() 添加内联附件,但操作起来有点复杂,需要使用一个遗留类。现在,你可以通过调用该方法并传入一个 Python 的 email.message.MIMEPart 对象,在几个步骤中添加内联附件:

python 复制代码
import email.utils
from email.message import MIMEPart
from django.core.mail import EmailMultiAlternatives

message = EmailMultiAlternatives(
    subject="可爱熊猫警报",
    body="这里有一张可爱的熊猫照片送给你!",
    from_email="cute@example.com",
    to=["fans@example.com"],
)
with open("panda.jpg", "rb") as f:
    panda_jpeg = f.read()

cid = email.utils.make_msgid()
inline_image = MIMEPart()
inline_image.set_content(
    panda_jpeg,
    maintype="image",
    subtype="jpeg",
    disposition="inline",
    cid=cid,
)
message.attach(inline_image)
message.attach_alternative(
    f'<h1>可爱熊猫宝宝警报!</h1><img src="cid:{cid[1:-1]}">',
    "text/html",
)

这个 API 并不简单,但它确实提供了底层邮件系统的全部功能,并且比过去的情况更好。

位置参数在 django.core.mail API 中的弃用

我们现在已经完成了主要功能,接下来是与上述邮件更改相关的"次要"更改,这是一个弃用警告:

django.core.mail API 现在要求对于不太常用的参数使用关键字参数。使用这些参数的位置参数现在会发出一个弃用警告,并且在弃用期结束后将引发一个 TypeError

  • 所有可选参数(fail_silently 及之后的参数)必须作为关键字参数传递给 get_connection()mail_admins()mail_managers()send_mail()send_mass_mail()
  • 在创建 EmailMessageEmailMultiAlternatives 实例时,除了前四个参数(subjectbodyfrom_emailto)可以作为位置参数或关键字参数传递外,所有其他参数都必须作为关键字参数传递。

以前,Django 允许你将所有参数作为位置参数传递,这在参数列表较长时会显得有些愚蠢且难以阅读,例如:

python 复制代码
from django.core.mail import send_mail

send_mail(
    "🐼 本周的熊猫",
    "本周的熊猫是 Po Ping,sha-sha booey!",
    "updates@example.com",
    ["adam@example.com"],
    True,
)

最后一个 True 没有提供任何线索,除非你查找函数签名。现在,使用这些不太常用的参数的位置参数会发出一个弃用警告,提示你写成如下形式:

python 复制代码
from django.core.mail import send_mail

send_mail(
    subject="🐼 本周的熊猫",
    body="本周的熊猫是 Po Ping,sha-sha booey!",
    from_email="updates@example.com",
    recipient_list=["adam@example.com"],
    fail_silently=True,
)

这种变化有助于提高 API 的清晰度,Django 正在逐步更多地使用仅关键字参数。django-upgrade 可以通过其 mail_api_kwargs 修复程序自动为你修复这一问题。

再次感谢 Mike Edmunds 在 Ticket #36163 中推动了这一改进。

扩展自动 shell 导入

接下来:

现在默认情况下,django.conf.settings 等常用工具会自动导入到 shell 中。

Django 5.2 的一个主要功能是 shell 中的自动模型导入,使得 ./manage.py shell 自动导入所有模型。在此基础上,Django 6.0 现在还导入了其他常用工具,我们可以通过运行 ./manage.py shell 并添加 -v 2 参数来查看完整的导入列表:

bash 复制代码
$ ./manage.py shell -v 2
6 objects imported automatically:

  from django.conf import settings
  from django.db import connection, models, reset_queries
  from django.db.models import functions
  from django.utils import timezone

...

(这是从一个没有模型的项目中得到的输出,因此只列出了工具。)

所以,这些包括:

  • settings:用于检查运行时配置,非常有用:
python 复制代码
In [1]: settings.DEBUG
Out[1]: False
python 复制代码
In [1]: Book.objects.select_related('author')
Out[1]: <QuerySet []>

In [2]: connection.queries
Out[2]:
[{'sql': 'SELECT "example_book"."id", "example_book"."title", "example_book"."author_id", "example_author"."id", "example_author"."name" FROM "example_book" INNER JOIN "example_author" ON ("example_book"."author_id" = "example_author"."id") LIMIT 21',
    'time': '0.000'}]
  • modelsfunctions:对于高级 ORM 工作非常有用:
python 复制代码
In [1]: Book.objects.annotate(
     ...:   title_lower=functions.Lower("title")
     ...: ).filter(
     ...:   title_lower__startswith="a"
     ...: ).count()
Out[1]: 71
  • timezone:用于使用 Django 的时区感知日期和时间工具:
python 复制代码
In [1]: timezone.now()
Out[1]: datetime.datetime(2025, 12, 1, 23, 42, 22, 558418, tzinfo=datetime.timezone.utc)

仍然可以按照 如何自定义 shell 命令 文档页面中所述,扩展自动导入内容。

Salvo Polizzi 在 Django 5.2 中贡献了原始的自动 shell 导入功能。然后,他在 Django 6.0 中又回来提供了这些额外的导入,在 Ticket #35680 中。感谢参与论坛讨论并达成一致意见的每个人,以及 Natalia Bidart 和 Sarah Boyce 的审查!

保存时动态字段刷新

现在让我们讨论一系列 ORM 改进,从这个重大改进开始:

在支持 RETURNING 子句的后端(SQLite、PostgreSQL 和 Oracle)上,GeneratedField 和被赋值为表达式的字段在调用 save() 之后会从数据库中刷新。在不支持它的后端(MySQL 和 MariaDB)上,字段会被标记为延迟加载,以在后续访问时触发刷新。

Django 模型支持在三种情况下让数据库为你生成字段值:

  1. db_default 字段选项,它允许数据库在创建实例时生成默认值:
python 复制代码
from django.db import models
from django.db.models.functions import Now

class Video(models.Model):
    ...
    created = models.DateTimeField(db_default=Now())
  1. GeneratedField 字段类型,它始终基于同一实例中的其他字段由数据库计算得出:
python 复制代码
from django.db import models
from django.db.models.functions import Concat

class Video(models.Model):
    ...
    full_title = models.GeneratedField(
        models.TextField(),
        expression=Concat(
            "title",
            models.Value(" - "),
            "subtitle",
        ),
    )
  1. 在保存之前为字段分配表达式值:
python 复制代码
from django.db import models
from django.db.models.functions import Now

class Video(models.Model):
    ...
    last_updated = models.DateTimeField()

video = Video.objects.get(id=1)
...
video.last_updated = Now()
video.save()

以前,只有第一种方法(使用 db_default)会在保存后从数据库中刷新字段值。其他两种方法会留下旧值或表达式对象,这意味着如果你需要获取更新后的值,就需要调用 Model.refresh_from_db()。这很难记住,并且会额外产生一个数据库查询。

现在,Django 利用 RETURNING SQL 子句在支持它的后端(SQLite、PostgreSQL 和 Oracle)上保存模型实例,并在单个查询中获取更新后的动态字段值。save() 调用现在可能会发出如下查询:

sql 复制代码
UPDATE "example_video"
SET "last_updated" = NOW()
WHERE "example_video"."id" = 1
RETURNING "example_video"."last_updated"

Django 将返回值放入模型字段中,因此你可以在保存后立即读取它:

python 复制代码
video = Video.objects.get(id=1)
...
video.last_updated = Now()
video.save()
print(video.last_updated)  # 数据库中的更新值

在不支持 RETURNING(MySQL 和 MariaDB)的后端上,Django 现在会在保存后将动态字段标记为延迟加载。这样,后续访问(如上面的示例)将自动调用 Model.refresh_from_db()。这确保了你总是读取更新后的值,即使这会额外产生一个查询。

历史

这个功能在 Ticket #27222 中于 2016 年被提出,由 Anssi Kääriäinen 提出。在过去的九年中,这个提议大部分时间都处于休眠状态,但 ORM 负责人 Simon Charette 今年早些时候接手了这个功能,找到了一个实现方案,并将其推动完成。感谢 Simon 继续推动 ORM 的发展,以及所有参与审查的人员:David Sanders、Jacob Walls、Mariusz Felisiak、nessita、Paolo Melchiorre、Simon Charette 和 Tim Graham。

通用 StringAgg 聚合函数

接下来是 ORM 的另一个变化:

新的 StringAgg 聚合函数将输入值连接成一个字符串,以 delimiter 字符串分隔。这个聚合函数以前仅支持 PostgreSQL。

这个聚合函数通常用于生成相关项目的逗号分隔列表等。以前,它仅作为 django.contrib.postgres 的一部分支持 PostgreSQL:

python 复制代码
from django.contrib.postgres.aggregates import StringAgg
from example.models import Video

videos = Video.objects.annotate(
    chapter_ids=StringAgg("chapter", delimiter=","),
)

for video in videos:
    print(f"Video {video.id} has chapters: {video.chapter_ids}")

这可能会输出如下内容:

less 复制代码
Video 104 has chapters: 71,72,74
Video 107 has chapters: 88,89,138,90,91,93

现在,这个聚合函数已经可以在 Django 支持的所有数据库后端中使用,从 django.db.models 中导入:

python 复制代码
from django.db.models import StringAgg, Value
from example.models import Video

videos = Video.objects.annotate(
    chapter_ids=StringAgg("chapter", delimiter=Value(",")),
)

for video in videos:
    print(f"Video {video.id} has chapters: {video.chapter_ids}")

注意,delimiter 参数现在需要一个 Value() 表达式包装器来处理字面字符串,如上所示。这种变化允许你使用数据库函数或字段作为分隔符(如果需要)。

虽然大多数 Django 项目都使用 PostgreSQL,但在所有后端上提供这个聚合函数是一个很好的改进,它提高了跨数据库的兼容性,并且意味着第三方包可以使用它而不会影响其数据库支持。

历史

PostgreSQL 特有的 StringAgg 最早在 Django 1.9(2015 年)由 Andriy Sokolovskiy 添加,见 Ticket #24301。在 Ticket #35444 中,Chris Muthig 提出了添加 Aggregate.order_by 选项,这是 StringAgg 用来指定连接元素顺序的选项,而作为副作用,这也使得将 StringAgg 推广到所有后端成为可能。

感谢 Chris 提出并实现了这一变化,以及所有参与审查的人员:Paolo Melchiorre、Sarah Boyce 和 Simon Charette。

BigAutoField 作为默认主键类型

接下来:

DEFAULT_AUTO_FIELD 设置的默认值现在改为 BigAutoField

这一重要变化有助于锁定更大规模的可扩展主键。

Django 3.2(2021 年)引入了 DEFAULT_AUTO_FIELD 设置,用于更改模型中使用的默认主键类型。Django 使用这个设置为未显式定义主键字段的模型添加一个名为 id 的主键字段。例如,如果你定义了一个模型如下:

python 复制代码
from django.db import models

class Video(models.Model):
    title = models.TextField()

那么它将有两个字段:idtitle,其中 id 使用 DEFAULT_AUTO_FIELD 定义的类型。

这个设置也可以通过在应用的 apps.py 文件中定义 AppConfig.default_auto_field 来在每个应用的基础上覆盖:

python 复制代码
from django.apps import AppConfig

class ChannelConfig(AppConfig):
    name = "channel"
    default_auto_field = "django.db.models.BigAutoField"

添加这个设置的一个主要动机是允许项目从 AutoField(32 位整数)切换到 BigAutoField(64 位整数)作为主键,而无需更改每个模型。AutoField 可以存储的值最多约为 21 亿,虽然听起来很大,但在大规模应用中很容易达到。BigAutoField 可以存储的值最多约为 92 京,这在实际用途中是"绰绰有余"的。

如果使用 AutoField 的模型达到了其最大值,它将无法再接受新行,这个问题被称为 主键耗尽 。该表实际上被阻塞了,需要通过锁定数据库迁移在大型表上紧急将模型从 AutoField 切换到 BigAutoField。关于 Kraken 如何修复这个问题,可以观看 Tim Bell 在 2025 年 DjangoCon Europe 的演讲,其中详细介绍了主动迁移大型表以减少停机时间的一些巧妙技术。

为了避免新项目出现这个问题,Django 3.2 使得通过 startproject 创建的新项目将 DEFAULT_AUTO_FIELD 设置为 BigAutoField,并且通过 startapp 创建的新应用将它们的 AppConfig.default_auto_field 设置为 BigAutoField。它还添加了一个系统检查,以确保项目显式设置 DEFAULT_AUTO_FIELD,以确保用户了解该功能并能够做出明智的选择。

现在,Django 6.0 将设置和应用配置属性的实际默认值改为 BigAutoField。使用 BigAutoField 的项目可以移除该设置:

python 复制代码
-DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"

以及应用配置属性:

python 复制代码
from django.apps import AppConfig

class ChannelConfig(AppConfig):
    name = "channel"
-    default_auto_field = "django.db.models.BigAutoField"

默认的 startprojectstartapp 模板也不再设置这些值。这一变化减少了新项目的样板代码,主键耗尽问题也将逐渐成为历史,成为大多数 Django 用户不再需要考虑的问题。

历史

在 Django 3.2 中添加 DEFAULT_AUTO_FIELD 是由 Caio Ariede 提出并由 Tom Forbes 实现的,见 Ticket #31007。Django 6.0 中的这一新变化是由前 Fellow Tim Graham 提出并实现的,见 Ticket #36564。感谢 Tim 发现现在可以进行这一清理工作,以及 Jacob Walls 和 Clifford Gama 的审查!

模板变量 forloop.length

继续来看模板方面的改进,首先是这个小而实用的新增功能:

for 循环中现在可以使用新的变量 forloop.length

这一小扩展使得你可以编写如下模板循环:

html 复制代码
<ul>
  {% for goose in geese %}
    <li>
      <strong>{{ forloop.counter }}/{{ forloop.length }}</strong>: {{ goose.name }}
    </li>
  {% endfor %}
</ul>

以前,你需要通过其他方式引用长度,例如 {{ geese|length }},这稍显不够灵活。

感谢 Jonathan Ströbele 在 Ticket #36186 中贡献了这个想法和实现,以及 David Smith、Paolo Melchiorre 和 Sarah Boyce 的审查。

querystring 模板标签增强

Django 5.1 中引入的 querystring 模板标签 用于帮助构建修改当前请求查询参数的链接,现在有了两个扩展。

  1. 发行说明:

querystring 模板标签现在始终在返回的查询字符串前加上 ?,确保链接生成行为的可靠性。

这一小变化改进了在提供空查询参数映射时标签的行为。假设你有一个模板如下:

django 复制代码
<a href="{% querystring params %}">Reset search</a>

其中 params 是一个可能有时为空的字典。以前,如果 params 为空,输出将是:

html 复制代码
<a href="">Reset search</a>

浏览器会将此视为指向相同 URL(包括查询参数)的链接,因此不会清除查询参数,这与预期不符。现在,有了这一变化,输出将是:

html 复制代码
<a href="?">Reset search</a>

浏览器会将 ? 视为指向相同 URL(不带任何查询参数)的链接,从而清除查询参数,符合用户的期望。

感谢 Django Fellow Sarah Boyce 发现这一改进并实现了修复,见 Ticket #36268,以及 Django Fellow Natalia Bidart 的审查!

  1. 发行说明:

querystring 模板标签现在可以接受多个位置参数,这些参数必须是映射,例如 QueryDictdict

这一增强功能允许标签在构建输出时合并多个查询参数来源。例如,你可能有一个模板如下:

django 复制代码
<a href="{% querystring request.GET super_search_params %}">Super search</a>

其中 super_search_params 是一个包含额外参数的字典,用于将当前搜索升级为"超级搜索"。该标签会合并这两个映射,对于重复的键,后面的映射将优先。

再次感谢 Sarah Boyce 在 Ticket #35529 中提出这一改进,Giannis Terzopoulos 实现了它,以及 Natalia Bidart、Sarah Boyce 和 Tom Carrick 的审查!

结语

以上就是本次 Django 6.0 的亮点总结!感谢你的阅读。更多变化可以在 发行说明 中查看。

此外,还有许多未进入发行说明的幕后改进和错误修复。优化和微小改进一直在被合并,所以不要延迟,现在就升级吧!

感谢为 Django 6.0 贡献的 174 人 ,这一数字由 Mariusz Felisiak 在 此列表 中统计。

愿你的升级过程迅速、顺利、安全且可靠!

最后感谢阅读!欢迎关注我,微信公众号: 倔强青铜三
点赞、收藏、关注 ,一键三连!!

欢迎 点击 【👍喜欢作者】 按钮进行 打赏💰💰,请我喝一杯咖啡☕️!

相关推荐
管理大亨2 小时前
ELK的操作应用
开发语言·python·elk
永远都不秃头的程序员(互关)2 小时前
深度学习入门:图像分类的实战应用
人工智能·机器学习
Mintopia2 小时前
🤖 AI 时代:Coding 如何约束智能体的任务正确率
人工智能·aigc·ai编程
kangk122 小时前
机器学习--序言
人工智能·机器学习
何小少2 小时前
从 Copilot 到 “Lab-pilot“:大语言模型在科学研究领域的应用现状与未来展望
人工智能·语言模型·copilot
攻城狮7号2 小时前
阿里推出Qwen-Image-i2L开源工具:AI绘画的个性化革命来了
人工智能·ai绘画·qwen-image-i2l·阿里开源工具
GISer_Jing2 小时前
AI赋能前端营销领域全解析:业务、技术、应用场景等
前端·人工智能
永远都不秃头的程序员(互关)2 小时前
零基础掌握AI:实战机器学习全流程
人工智能·机器学习
神算大模型APi--天枢6462 小时前
从异构调度到边缘部署:国产大模型算力平台的后端开发能力拆解
大数据·人工智能·科技·架构·硬件架构