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 时区,并使用 pytznormalize()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_shimUSE_DEPRECATED_PYTZ 过渡性设置。

函数式唯一约束

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

  1. from django.db import models
  2. from django.db.models import UniqueConstraint
  3. from django.db.models.functions import Lower
  4. class MyModel(models.Model):
  5. first_name = models.CharField(max_length=255)
  6. last_name = models.CharField(max_length=255)
  7. class Meta:
  8. constraints = [
  9. UniqueConstraint(
  10. Lower("first_name"),
  11. Lower("last_name").desc(),
  12. name="first_last_name_unique",
  13. ),
  14. ]

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

scrypt 密码哈希器

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

Redis 缓存后端

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

基于模板的表单渲染

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

次要特性

django.contrib.admin

django.contrib.admindocs

  • admindocs 现在允许复杂的设置,其中 ROOT_URLCONF 不是一个字符串。
  • admindocs 的模型部分现在显示缓存的属性。

django.contrib.auth

django.contrib.gis

  • 新增对 SpatiaLite 5 的支持。
  • GDALRaster 现在允许在任何 GDAL 虚拟文件系统中创建栅格数据。
  • 新的 GISModelAdmin 类允许自定义用于 GeometryField 的小部件,这是鼓励使用的,而不是不推荐使用的 GeoModelAdminOSMGeoAdmin

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 的子类,例如带有复选框的表单,来确认删除。此外,这还允许 DeleteViewdjango.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.stdoutprint()

模板

  • 模板过滤器 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' 脚本。

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

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

迁移自动检测器的更改

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

作为副作用,在某些情况下,运行 makemigrations 可能会为 ManyToManyFieldForeignKey 字段生成无操作的 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
  • 如果使用无效的 contentmimetype 参数调用 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
  • RssFeedAtom1Feed 及其子类现在将没有内容的元素作为自关闭标签进行输出。
  • NodeList.render() 不再将单个节点的 render() 方法的输出转换为字符串。根据文档,Node.render() 应始终返回一个字符串。
  • django.db.models.sql.query.Querywhere_class 属性以及 ForeignObjectForeignObjectRel 的私有方法 get_extra_restriction()where_class 参数已被移除。如果需要,可以初始化 django.db.models.sql.where.WhereNode
  • 未记录的 Query.add_filter() 方法的 filter_clause 参数被替换为两个位置参数 filter_lhsfilter_rhs
  • CsrfViewMiddleware 现在使用 request.META['CSRF_COOKIE_NEEDS_UPDATE'] 代替 request.META['CSRF_COOKIE_USED']request.csrf_cookie_needs_resetresponse.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),如果提供的话。
  • RadioSelectCheckboxSelectMultiple 小部件现在在 <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 中,当没有行时,ArrayAggJSONBAggStringAgg 聚合将返回 None,而不是分别返回 [][]''。如果需要以前的行为,请显式将 default 设置为 Value([])Value('[]')Value('')
  • django.contrib.gis.admin.GeoModelAdminOSMGeoAdmin 类已被弃用。请改为使用 ModelAdminGISModelAdmin
  • 由于表单渲染现在使用模板引擎,未记录的 BaseForm._html_output() 辅助方法已被弃用。
  • ErrorListErrorDict 返回 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=Nonedjango.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.Signalproviding_args 参数已被移除。
  • django.utils.crypto.get_random_string()length 参数是必需的。
  • ModelMultipleChoiceFieldlist 消息已被移除。
  • 不再支持将原始列别名传递给 QuerySet.order_by()
  • NullBooleanField 模型字段已被移除,除了在历史迁移中的支持。
  • django.conf.urls.url() 已被移除。
  • django.contrib.postgres.fields.JSONField 模型字段已被移除,除了在历史迁移中的支持。
  • django.contrib.postgres.fields.jsonb.KeyTransformdjango.contrib.postgres.fields.jsonb.KeyTextTransform 已被移除。
  • django.contrib.postgres.forms.JSONField 已被移除。
  • {% ifequal %}{% ifnotequal %} 模板标签已被移除。
  • DEFAULT_HASHING_ALGORITHM 过渡设置已被移除。