Django 4.0 版本发行说明

2021 年 12 月 7 日

欢迎使用 Django 4.0 !

这些发布说明涵盖了 新功能,以及从 Django 3.2 或更早版本升级时需要注意的一些 不向后兼容的变更。我们已经 开始了一些功能的弃用过程。

如果你要更新现有的项目,请看 如何将 Django 更新至新的版本 指南。

Python 兼容性

Django 4.0 支持 Python 3.8、3.9 和 3.10。我们 强烈建议 并只官方支持每个系列的最新版本。

Django 3.2.x 系列是最后一个支持 Python 3.6 和 3.7 的版本。

Django 4.0 新特性

zoneinfo 默认时区实现

Python 标准库的 zoneinfo 现在是 Django 中默认的时区实现。

这是从使用 pytz 到使用 zoneinfo 迁移的下一步。Django 3.2 允许使用非 pytz 时区。Django 4.0 将 zoneinfo 作为默认实现。对于 pytz 的支持现在已经不推荐使用,并将在 Django 5.0 中移除。

zoneinfo 是自 Python 3.9 起的 Python 标准库的一部分。如果你正在使用 Python 3.8,那么 backports.zoneinfo 包会在安装 Django 时自动安装。

迁移到 zoneinfo 应该在很大程度上是透明的。选择当前时区、在表单和模板中将日期时间实例转换为当前时区,以及在 UTC 中进行的带时区日期时间的操作都不受影响。

然而,如果你正在使用非 UTC 时区,并使用 pytz 的 normalize() 和 localize() API,可能还涉及到 TIME_ZONE 设置,那么你需要审查你的代码,因为``pytz`` 和 zoneinfo 并不完全等同。

为了给这样的审核留出时间,过渡性的 USE_DEPRECATED_PYTZ 设置允许在 4.x 发布周期内继续使用 pytz。这个设置将在 Django 5.0 中移除。

此外,zoneinfo 的作者创建了一个名为 pytz_deprecation_shim 的包,可用于协助从 pytz 迁移。这个包提供了帮助你安全移除 pytz 的 shims,并有一个详细的 迁移指南,展示了如何切换到新的 zoneinfo API。

如果需要逐步更新路径,建议使用 pytz_deprecation_shim 和过渡性的 USE_DEPRECATED_PYTZ 设置。

函数式唯一约束

UniqueConstraint() 的新的 *expressions 位置参数允许在表达式和数据库函数上创建功能性的唯一约束。例如:

from django.db import models
from django.db.models import UniqueConstraint
from django.db.models.functions import Lower


class MyModel(models.Model):
    first_name = models.CharField(max_length=255)
    last_name = models.CharField(max_length=255)

    class Meta:
        constraints = [
            UniqueConstraint(
                Lower("first_name"),
                Lower("last_name").desc(),
                name="first_last_name_unique",
            ),
        ]

功能性唯一约束是通过在模型中使用 Meta.constraints 选项来添加的。

scrypt 密码哈希器

新的 scrypt 密码哈希器 更安全,推荐使用,但它不是默认选项,因为它需要 OpenSSL 1.1+ 和更多内存。

Redis 缓存后端

新的 django.core.cache.backends.redis.RedisCache 缓存后端提供了内置的支持,用于使用 Redis 进行缓存。需要 redis-py 的版本为 3.0.0 或更高。更多详情请参阅 Django 中关于使用 Redis 进行缓存的 文档。

基于模板的表单渲染

Forms、Formsets 和 ErrorList 现在使用模板引擎进行渲染,以增强自定义功能。查看 Form 的新方法 render()、get_context() 和 template_name,以及关于 Formset 的 formset 渲染。

次要特性

django.contrib.admin

django.contrib.admindocs

  • admindocs 现在允许复杂的设置,其中 ROOT_URLCONF 不是一个字符串。

  • admindocs 的模型部分现在显示缓存的属性。

django.contrib.auth

django.contrib.gis

  • 新增对 SpatiaLite 5 的支持。

  • GDALRaster 现在允许在任何 GDAL 虚拟文件系统中创建栅格数据。

  • 新的 GISModelAdmin 类允许自定义用于 GeometryField 的小部件,这是鼓励使用的,而不是不推荐使用的 GeoModelAdmin 和 OSMGeoAdmin。

django.contrib.postgres

django.contrib.staticfiles

缓存

  • django.core.cache.backends.base.BaseCache 的新异步 API 开始了使缓存后端支持异步的过程。新的异步方法都有 a 前缀的名称,例如 aadd()、aget()、aset()、aget_or_set() 或 adelete_many()。

    今后,a 前缀将用于一般方法的异步变体。

CSRF

表单

国际化

  • 添加了对马来语的支持和翻译。

通用视图

  • DeleteView 现在使用 FormMixin,允许你提供一个 Form 的子类,例如带有复选框的表单,来确认删除。此外,这还允许 DeleteView 与 django.contrib.messages.views.SuccessMessageMixin 配合使用。

    根据 FormMixin,对于 POST 请求的对象删除是在 form_valid() 中处理的。如果需要,应将 delete() 处理程序中的自定义删除逻辑移动到 form_valid() 或共享的辅助方法中。

日志

  • 现在,用于 SQL 调用的数据库的别名会作为额外的上下文与每条消息一起传递到 django.db.backends 记录器中。

管理命令

模型

  • 新的 QuerySet.contains(obj) 方法返回查询集是否包含给定对象。它尝试以最简单和最快的方式执行查询。

  • Round() 数据库函数的新 precision 参数允许指定舍入后的小数位数。

  • 使用 SQLite 3.35+ 时,QuerySet.bulk_create() 现在会为对象设置主键。

  • DurationField 现在在 SQLite 上支持乘法和除法以及标量值。

  • QuerySet.bulk_update() 现在会返回更新的对象数量。

  • 新的 Expression.empty_result_set_value 属性允许在函数用于空结果集时指定要返回的值。

  • 在 MariaDB 10.6+ 上,现在允许使用 QuerySet.select_for_update() 的 skip_locked 参数。

  • Lookup 表达式现在可以在 QuerySet 的注释、聚合和直接筛选中使用。

  • 内置聚合函数的新 default 参数允许指定当查询集(或分组)不包含条目时要返回的值,而不是返回 None。

请求和响应

信号

  • pre_migrate() 和 post_migrate() 信号现在有一个新的 stdout 参数,允许将输出重定向到类似流的对象。在发出详细输出时,应优先考虑使用它,以便在测试时能够正确捕获输出,而不是使用 sys.stdout 和 print()。

模板

  • 模板过滤器 floatformat 现在允许使用 u 后缀来强制禁用本地化。

测试

4.0 版本中不向后兼容的变更

数据库后端 API

本节介绍了第三方数据库后端可能需要的更改。

  • DatabaseOperations.year_lookup_bounds_for_date_field() 和 year_lookup_bounds_for_datetime_field() 方法现在接受可选的 iso_year 参数,以支持 ISO-8601 周编号年份的范围。

  • DatabaseSchemaEditor._unique_sql() 和 _create_unique_sql() 方法的第二个参数现在是 fields,而不再是 columns。

django.contrib.gis

  • 移除了对 PostGIS 2.3 的支持。

  • 移除了对 GDAL 2.0 和 GEOS 3.5 的支持。

停止支持 PostgreSQL 9.6

PostgreSQL 9.6 的上游支持将在 2021 年 11 月结束。 Django 4.0 支持 PostgreSQL 10 及更高版本。

此外,psycopg2 的最低支持版本已从 2.5.4 提高到 2.8.4,因为 psycopg2 2.8.4 是第一个支持 Python 3.8 的版本。

停止支持 Oracle 12.2 和 18c

Oracle 12.2 的上游支持将于 2022 年 3 月结束,Oracle 18c 的上游支持将于 2021 年 6 月结束。 Django 3.2 将得到支持直至 2024 年 4 月。 Django 4.0 正式支持 Oracle 19c 。

CSRF_TRUSTED_ORIGINS 变更

格式变更

在 CSRF_TRUSTED_ORIGINS 设置中,值必须包括方案(例如,'http://' 或 'https://'),而不仅仅是主机名。

此外,以点开头的值现在必须在点之前加上一个星号。例如,将 '.example.com' 更改为 'https://*.example.com'。

系统检查会检测到任何所需的更改。

现在可能需要配置它

由于 CSRF 保护现在参考 Origin 标头,如果你通过将 CSRF_COOKIE_DOMAIN (或者如果启用了 CSRF_USE_SESSIONS,则为 SESSION_COOKIE_DOMAIN)设置为以点开头的值以允许来自子域的请求,你可能需要设置 CSRF_TRUSTED_ORIGINS。

SecurityMiddleware 不再设置 X-XSS-Protection 头

如果 SECURE_BROWSER_XSS_FILTER 设置为 True,则 SecurityMiddleware 不再设置 X-XSS-Protection 头。此设置已被移除。

大多数现代浏览器不再支持 X-XSS-Protection HTTP 头。你可以使用 Content-Security-Policy 来代替,但请不要允许 'unsafe-inline' 脚本。

如果您想要支持旧版浏览器并设置标头,请在自定义中间件中使用以下代码行:

response.headers.setdefault("X-XSS-Protection", "1; mode=block")

迁移自动检测器的更改

迁移自动检测器现在使用模型状态而不是模型类。此外,对于 ForeignKey 和 ManyToManyField 字段的迁移操作不再指定未在字段初始化期间传递的属性。

作为副作用,在某些情况下,运行 makemigrations 可能会为 ManyToManyField 和 ForeignKey 字段生成无操作的 AlterField 操作。

DeleteView 变更

DeleteView 现在使用 FormMixin 来处理 POST 请求。因此,任何在 delete() 处理程序中的自定义删除逻辑应该移动到 form_valid(),或者如果需要的话,可以移动到共享的辅助方法。

Oracle 上的表和列命名方案发生了变化。

Django 4.0 无意中更改了 Oracle 上的表和列命名方案。这会导致模型和字段的名称超过 30 个字符的错误。不幸的是,需要重命名一些 Oracle 表和列。请使用 33789 中的升级脚本生成 RENAME 语句来更改命名方案。

杂项

  • 已移除对 cx_Oracle 版本低于 7.0 的支持。

  • 为了允许在子路径上提供 Django 站点而不更改 STATIC_URL 的值,现在在默认的 startproject 模板中从该设置中移除了前导斜杠(现在是 'static/')。

  • 当直接访问 AdminSite 的管理 index 视图时,不再使用 never_cache 进行装饰,而是建议通过 AdminSite.urls 属性或 AdminSite.get_urls() 方法访问。

  • 对切片的查询集执行不支持的操作现在会引发 TypeError 而不是 AssertionError。

  • 未记录的 django.test.runner.reorder_suite() 函数已更名为 reorder_tests()。现在它接受一个测试的可迭代对象而不是测试套件,并返回一个测试的迭代器。

  • 现在,如果使用空的 name 调用 FileSystemStorage.delete(),会引发 ValueError 而不是 AssertionError。

  • 如果使用无效的 content 或 mimetype 参数调用 EmailMultiAlternatives.attach_alternative() 或 EmailMessage.attach(),现在会引发 ValueError 而不是 AssertionError。

  • assertHTMLEqual() 不再将没有值的非布尔属性视为与具有相同名称和值的属性相等。

  • 现在,无法加载的测试,例如由于语法错误等原因,使用 test --tag 时始终匹配。

  • 未记录的 django.contrib.admin.utils.lookup_needs_distinct() 函数已更名为 lookup_spawns_duplicates()。

  • 未记录的 HttpRequest.get_raw_uri() 方法已被移除。 HttpRequest.build_absolute_uri() 方法可能是一个合适的替代方法。

  • 未记录的 ModelAdmin.log_addition()、log_change() 和 log_deletion() 方法的 object 参数已更名为 obj。

  • RssFeed、Atom1Feed 及其子类现在将没有内容的元素作为自关闭标签进行输出。

  • NodeList.render() 不再将单个节点的 render() 方法的输出转换为字符串。根据文档,Node.render() 应始终返回一个字符串。

  • django.db.models.sql.query.Query 的 where_class 属性以及 ForeignObject 和 ForeignObjectRel 的私有方法 get_extra_restriction() 的 where_class 参数已被移除。如果需要,可以初始化 django.db.models.sql.where.WhereNode。

  • 未记录的 Query.add_filter() 方法的 filter_clause 参数被替换为两个位置参数 filter_lhs 和 filter_rhs。

  • CsrfViewMiddleware 现在使用 request.META['CSRF_COOKIE_NEEDS_UPDATE'] 代替 request.META['CSRF_COOKIE_USED']、request.csrf_cookie_needs_reset 和 response.csrf_cookie_set 来跟踪 CSRF cookie 是否应该发送。这是一个未记录的私有 API。

  • 未记录的 TRANSLATOR_COMMENT_MARK 常量已从 django.template.base 移动到 django.utils.translation.template。

  • 未记录的 django.db.migrations.state.ProjectState.__init__() 方法的 real_apps 参数现在必须是一个集合(set),如果提供的话。

  • RadioSelect 和 CheckboxSelectMultiple 小部件现在在 <div> 标签中呈现,以便屏幕阅读器更简洁地宣读它们。如果需要以前的行为,请使用来自 Django 3.2 的适当模板 覆盖小部件模板。

  • 模板过滤器 floatformat 不再依赖于 USE_L10N 设置,并始终返回本地化的输出。使用 u 后缀来禁用本地化。

  • USE_L10N 设置的默认值已更改为 True。有关更多详细信息,请参阅上面的 本地化部分。

  • 作为 迁移到 zoneinfo 的一部分,django.utils.timezone.utc 被更改为别名 datetime.timezone.utc。

  • asgiref 的最低支持版本已从 3.3.2 增加到 3.4.1。

在 4.0 版本中废弃的功能

使用 pytz 时区

作为 迁移到 zoneinfo 的一部分,不推荐使用 pytz 时区。

相应地,以下函数的 is_dst 参数也已被弃用:

在 Django 5.0 中将移除对 pytz 的支持。

时区支持

为了遵循良好的实践,USE_TZ 设置的默认值将从 False 更改为 True,并且在 Django 5.0 中将默认启用时区支持。

请注意,由 django-admin startproject 创建的默认 settings.py 文件自 Django 1.4 起就包括 USE_TZ = True。

你可以在那之前在项目设置中将 USE_TZ 设置为 False 以选择不使用时区支持。

本地化

为了遵循良好的实践,USE_L10N 设置的默认值从 False 更改为 True。

此外,从此版本开始,USE_L10N 已被弃用。从 Django 5.0 开始,默认情况下,Django 显示的任何日期或数字都将进行本地化。

Django 仍将支持 {% localize %} 标签以及 localize/ unlocalize 过滤器。

杂项

  • SERIALIZE 测试设置已被弃用,因为可以从启用 serialized_rollback 选项的 databases 推断出。

  • 未记录的 django.utils.baseconv 模块已被弃用。

  • 未记录的 django.utils.datetime_safe 模块已被弃用。

  • 在 Django 5.0 中,构建在请求上下文之外的站点地图的默认协议将从 'http' 更改为 'https'。

  • DiscoverRunner.build_suite() 和 DiscoverRunner.run_tests() 的 extra_tests 参数已被弃用。

  • 在 Django 5.0 中,当没有行时,ArrayAgg、JSONBAgg 和 StringAgg 聚合将返回 None,而不是分别返回 []、[] 和 ''。如果需要以前的行为,请显式将 default 设置为 Value([])、Value('[]') 或 Value('')。

  • django.contrib.gis.admin.GeoModelAdmin 和 OSMGeoAdmin 类已被弃用。请改为使用 ModelAdmin 和 GISModelAdmin。

  • 由于表单渲染现在使用模板引擎,未记录的 BaseForm._html_output() 辅助方法已被弃用。

  • 从 ErrorList 和 ErrorDict 返回 str 的能力已被弃用。预期这些方法返回一个 SafeString。

在 4.0 版本中移除的功能

这些功能已经完成了废弃周期,并在 Django 4.0 中被移除。

请参阅 在 3.0 中被废弃的功能 以获取有关这些更改的详细信息,包括如何删除对这些功能的使用。

  • django.utils.http.urlquote(), urlquote_plus(), urlunquote(), 和 urlunquote_plus() 已被移除。

  • django.utils.encoding.force_text() 和 smart_text() 已被移除。

  • django.utils.translation.ugettext(), ugettext_lazy(), ugettext_noop(), ungettext(), 和 ungettext_lazy() 已被移除。

  • django.views.i18n.set_language() 不会在 request.session 中设置用户语言(键为 _language)。

  • alias=None 在 django.db.models.Expression.get_group_by_cols() 子类的签名中是必需的。

  • django.utils.text.unescape_entities() 已被移除。

  • django.utils.http.is_safe_url() 已被移除。

请参阅 在 3.1 中被废弃的功能 以获取有关这些更改的详细信息,包括如何删除对这些功能的使用。

  • PASSWORD_RESET_TIMEOUT_DAYS 设置已被移除。

  • isnull 查询不再允许使用非布尔值作为右侧的值。

  • django.db.models.query_utils.InvalidQuery 异常类已被移除。

  • django-admin.py 入口点已被移除。

  • HttpRequest.is_ajax() 方法已被移除。

  • 已移除对由 django.contrib.messages.storage.cookie.CookieStorage 使用的 Django 3.1 之前版本的 cookie 值编码格式的支持。

  • 移除了对使用 SHA-1 哈希算法的 Django 3.1 之前版本密码重置令牌在管理站点的支持。

  • 移除了对使用 Django 3.1 之前编码格式的会话的支持。

  • 已移除对 Django 3.1 之前版本中使用 SHA-1 算法编码的 django.core.signing.Signer 签名的支持。

  • 已移除在 django.core.signing.loads() 中对 Django 3.1 之前版本中使用 SHA-1 算法编码的 django.core.signing.dumps() 签名的支持。

  • 移除了对使用 SHA-1 算法的 Django 3.1 之前用户会话的支持。

  • django.utils.deprecation.MiddlewareMixin.__init__() 的 get_response 参数是必需的,并且不接受 None。

  • django.dispatch.Signal 的 providing_args 参数已被移除。

  • django.utils.crypto.get_random_string() 的 length 参数是必需的。

  • ModelMultipleChoiceField 的 list 消息已被移除。

  • 不再支持将原始列别名传递给 QuerySet.order_by()。

  • NullBooleanField 模型字段已被移除,除了在历史迁移中的支持。

  • django.conf.urls.url() 已被移除。

  • django.contrib.postgres.fields.JSONField 模型字段已被移除,除了在历史迁移中的支持。

  • django.contrib.postgres.fields.jsonb.KeyTransform 和 django.contrib.postgres.fields.jsonb.KeyTextTransform 已被移除。

  • django.contrib.postgres.forms.JSONField 已被移除。

  • {% ifequal %} 和 {% ifnotequal %} 模板标签已被移除。

  • DEFAULT_HASHING_ALGORITHM 过渡设置已被移除。