Alan Hou的个人博客

Django 4.0正式发布、主要特性概述

Django 4.0正式发布、主要特性概述

2021年12月7日,Django 团队正式发布了 Django 4.0。主要新增功能有:

安装方式:

4.0的发布也意味着3.2成为了LTS 版本,3.2的安全问题修复将持续至2024年4月,所以大可不必着急在生产上升级版本。但使用Django 3.1的朋友就要注意了,因为该版本已终止了官方维护,请尽快升级至3.2。

Django 4.0版本有以下值得注意的地方:

Python兼容性

Django 4.0兼容Python 3.8, 3.9和3.10。强烈建议使用这些版本的最新官方发行包。这也意味着Django 3.2.x系列是对Python 3.6 和 3.7进行支持的最后一版 Django。

Django 4.0的新特性

zoneinfo 成为默认时区实现

Python标准库中的zoneinfo 目前成为了 Django 对时区的默认实现。

使用zoneinfo是对迁移pytz的更进一步实现。Django 3.2中允许使用非pytz 时区,Django 4.0则让 zoneinfo 成为了默认实现。对pytz 的支持已降级并且在Django 5.0中会彻底移除。

zoneinfo 自Python 3.9加入了Python的标准库。使用Python 3.8就会在安装 Django 时自动安装 backports.zoneinfo 包。

采用 zoneinfo大多地方都是显而易见的。 对当前时区的选择、在表单和模板中将datetime实例转化当前时区以及UTC时间的操作均不受影响。

但如果使用的是非UTC时区,配置了TIME_ZONE以及及使用了 pytz normalize() 和 localize() API的话,则需要审计下代码,因为pytz 和 zoneinfo 并非完全一致。

为了对于这类审计保留时间,临时使用 USE_DEPRECATED_PYTZ 设置可允许在4.x 中继续使用 pytz 。但这一设置会在Django 5.0中移除。

此外,zoneinfo作者创建的pytz_deprecation_shim 包,也可用于辅助对pytz 的迁移。这个包提供了一个 shim 辅助用户安全地移除掉 pytz,并且提供了详细的迁移指南 讲解如何迁移至新的 zoneinfo API。

如果采用渐进升级策略,建议使用 pytz_deprecation_shim 和 USE_DEPRECATED_PYTZ 临时配置。

Functional unique constraints

UniqueConstraint()中新的 *expressions 位置参数让我们可以用表达式及数据库函数创建唯一性约束。例如:

使用Meta.constraints 选项对模型添加了唯一性索引。

scrypt 密码哈希工具

新的 scrypt密码哈希工具 更加案例,也是PBKDF2加密算法中所推荐使用中。但它并非默认工具,因为要求使用OpenSSL 1.1+版本同时也更耗内存。

Redis后端缓存

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

表单的模板渲染

FormsFormsetsErrorList 现在使用模板引擎来改善其自定义特性。参见 Form 的新API 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的自定义组件。推荐使用它替代老的GeoModelAdmin 和 OSMGeoAdmin

django.contrib.postgres

django.contrib.staticfiles

缓存

  • django.core.cache.backends.base.BaseCache 的新async API开始让后端缓存操作兼容异步。新的异步方法都带有 a 作为前缀,如 aadd()aget()aset()aget_or_set()或 adelete_many()

    推而广之,前缀 a 通常用于方法的异步变体。

CSRF

Forms

国际化

  • 新增了对马来语的支持和翻译。

通用视图

  • DeleteView 现在使用 FormMixin,可由我们提供一个比如带有复选框的 Form 子类 ,来确认删除。此外,这会让 DeleteView 可与 django.contrib.messages.views.SuccessMessageMixin配合使用。

    对应FormMixin, POST请求的对象删除在 form_valid()中进行处理。 delete() handler中的自定义删除逻辑应迁移至form_valid()或按需移至一个共用帮助方法中。

日志

  • SQL调用中使用的数据库别名可与消息一起由附加的上下文传递给 django.db.backends 日志工具。

管理合集

模型

  • 新方法 QuerySet.contains(obj) 返回queryset中是否包含指定对象。旨在尝试让查询变得尽可能简单、快捷。
  • Round() 数据函数的新参数precision可指定四舍五入之后的小数位数。
  • QuerySet.bulk_create() 在SQLite 3.35+版本时为对象设置主键。
  • DurationField 支持对SQLite中的标量值进行乘除。
  • QuerySet.bulk_update()返回更新了的对象数。
  • 新属性 Expression.empty_result_set_value 支持在对空结果集执行函数时返回的值。
  • 支持在MariaDB 10.6+版本中对QuerySet.select_for_update() 使用skip_locked参数。
  • 支持QuerySet 注解、聚合运算和过滤器中直接使用Lookup表达式。
  • 内置聚合运算新增 default 参数,可以指定queryset(或分组)不包含内容时返回某个值,替换掉 None

请求和响应

信号

模板

  • 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() 方法现在是 字段,不再是

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及更高版本。

移除对Oracle 12.2 和 18c的支持

Oracle 12.2的上游支持于2022年终结,而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_TRUSTED_ORIGINS,尤其是在允许子域进行请求时,方式为设置 CSRF_COOKIE_DOMAIN (或在开启CSRF_USE_SESSIONS 时设置SESSION_COOKIE_DOMAIN ) 为一个以点号开头的值。

SecurityMiddleware 不再设置X-XSS-Protection

在设置SECURE_BROWSER_XSS_FILTER 为 TrueSecurityMiddleware不再设置X-XSS-Protection头。 该设置已被移除。

大部分现代浏览器不使用X-XSS-Protection HTTP头。可以使用Content-Security-Policy 并不允许使用 'unsafe-inline' 脚本。

如果希望兼容老浏览器、设置这个头,可使用如下的自定义中间件:

Migration自动检测的调整

migration的autodetector使用模型状态取代模型类。同时对 ForeignKey 和 ManyToManyField字段的迁移操作不再指定初始化阶段不传入这些字段的属性。

负面效应是在某些情况下,对 ManyToManyField 和 ForeignKey 字段运行makemigrations时可能会生成无操作的AlterField

DeleteView 的调整

DeleteView 现在使用 FormMixin 来处理POST 请求。因此 delete() handler中的所有自定义删除逻辑都应迁移至 form_valid()或是共享帮助方法中。

其它

  • 删除对 cx_Oracle 7.0以下版本的支持。
  • 支持通过子路径访问Django站点而无需修改 STATIC_URL,在默认的startproject模板中也去除了前置斜线的设置 (当前为 'static/')。
  • 后台 index 视图的AdminSite方法在直接访问时不再使用never_cache进行装饰,无需使用原来推荐的 AdminSite.urls 属性或 AdminSite.get_urls() 方法。
  • 对queryset切片所不支持的操作会抛出 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
  • RssFeedAtom1Feed及它们的子类对无内容的元素会转为自关闭标签。
  • 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。这是一个记录在文档中的私有API。
  • 未放入文档的TRANSLATOR_COMMENT_MARK 常量由django.template.base 迁移至 django.utils.translation.template
  • 未入文档的django.db.migrations.state.ProjectState.__init__() 方法的参数real_apps在提供时必须要设置。
  • RadioSelect 和 CheckboxSelectMultiple 组件在 <div>标签中进行渲染,这样屏幕阅读器可以更精准地读取。如果需要使用之前的功能,可通过Django 3.2中相应的模块重写这一组件模板
  • floatformat模板过滤器不再依赖于USE_L10N 配置并总是返回本地化输出。使用后缀 u 来禁用本地化。
  • USE_L10N 配置的默认值修改为了 True。参见上面的本地化一节 了解更多详情。
  • 迁移至zoneinfodjango.utils.timezone.utc 修改为了别名 datetime.timezone.utc
  • asgiref 的最小兼容版本由3.3.2 提升为 3.4.1。

4.0中淘汰的功能

时区支持

为遵循最佳实践, 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 %} 标签和 localizeunlocalize 过滤器。

其它

  • SERIALIZE测试已淘汰,因为可通过在databases中启用 serialized_rollback选项来进行推导。
  • 淘汰未入文档的 django.utils.baseconv 模块。
  • 淘汰未入文档的django.utils.datetime_safe 模块。
  • 请求上下文外构建网站地图的默认 sitemap协议将在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.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)中的用户语言。
  • django.db.models.Expression.get_group_by_cols() 子类签名中必须包含alias=None
  • 删除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 3.1之前django.contrib.messages.storage.cookie.CookieStorage所使用的cookie值编码格式的支持。
  • 删除了对Django 3.1版本前后台站点密码重置token的支持(使用的是 SHA-1 哈希算法) 。
  • 删除了对Django 3.1版本之前会话编码格式的支持。
  • 删除了对Django 3.1版本之前django.core.signing.Signer 签名的支持(以SHA-1进行编码)。
  • 删除了Django 3.1版本之前 django.core.signing.loads()django.core.signing.dumps()签名的支持(以SHA-1进行编码)。
  • 删除了对Django 3.1之前(使用SHA-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.KeyTransform 和 django.contrib.postgres.fields.jsonb.KeyTextTransform
  • 删除了django.contrib.postgres.forms.JSONField
  • 删除了 {% ifequal %} 和 {% ifnotequal %} 模板标签。
  • 删除了 DEFAULT_HASHING_ALGORITHM 临时配置。
退出移动版