作者: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.html。video_view_count 视图通过在模板名称后附加 #view_count 来仅渲染 view_count 局部定义。这种语法类似于在 URL 中通过其 ID 引用 HTML 片段的方式。
任务框架(Tasks Framework)
Django 现在包含了一个内置的任务框架,用于在 HTTP 请求 - 响应周期之外运行代码。这使得可以将工作(如发送电子邮件或处理数据)卸载到后台工作程序中。
本质上,这是一个用于定义和排队后台任务的新 API------非常酷!
后台任务是一种在请求 - 响应周期之外运行代码的方式。它们是 Web 应用程序中的常见需求,用于发送电子邮件、处理图像、生成报告等。
历史上,Django 没有提供任何后台任务系统,而是完全忽略了这个问题。开发人员依赖于第三方包,如 Celery 或 Django 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 类型。
现代化带来了以下好处:
- 更少的错误:Python 旧邮件 API 中的许多边缘情况错误已在新 API 中修复。
- Django 更简洁:Django 邮件代码中的一系列变通方法和安全修复已被移除。
- 更方便的 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.mailAPI 现在要求对于不太常用的参数使用关键字参数。使用这些参数的位置参数现在会发出一个弃用警告,并且在弃用期结束后将引发一个TypeError:
- 所有可选参数(
fail_silently及之后的参数)必须作为关键字参数传递给get_connection()、mail_admins()、mail_managers()、send_mail()和send_mass_mail()。- 在创建
EmailMessage或EmailMultiAlternatives实例时,除了前四个参数(subject、body、from_email和to)可以作为位置参数或关键字参数传递外,所有其他参数都必须作为关键字参数传递。
以前,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
connection和reset_queries():非常适合 检查执行的查询:
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'}]
models和functions:对于高级 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 模型支持在三种情况下让数据库为你生成字段值:
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())
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",
),
)
- 在保存之前为字段分配表达式值:
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()
那么它将有两个字段:id 和 title,其中 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"
默认的 startproject 和 startapp 模板也不再设置这些值。这一变化减少了新项目的样板代码,主键耗尽问题也将逐渐成为历史,成为大多数 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 模板标签 用于帮助构建修改当前请求查询参数的链接,现在有了两个扩展。
- 发行说明:
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 的审查!
- 发行说明:
querystring模板标签现在可以接受多个位置参数,这些参数必须是映射,例如QueryDict或dict。
这一增强功能允许标签在构建输出时合并多个查询参数来源。例如,你可能有一个模板如下:
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 在 此列表 中统计。
愿你的升级过程迅速、顺利、安全且可靠!
最后感谢阅读!欢迎关注我,微信公众号: 倔强青铜三 。
点赞、收藏、关注 ,一键三连!!欢迎 点击 【👍喜欢作者】 按钮进行 打赏💰💰,请我喝一杯咖啡☕️!