Django 4.2 版本发行说明

2023 年 4 月 3 日

欢迎使用 Django 4.2 !

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

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

Django 4.2 被指定为 长期支持版本。它将在发布后至少三年内接收安全更新。对于之前的 LTS 版本 Django 3.2,支持将在 2024 年 4 月结束。

Python 兼容性

Django 4.2 支持 Python 3.8、3.9、3.10、3.11 和 3.12(截止到 4.2.8 版本)。我们 强烈推荐 并且只官方支持每个系列的最新版本。

Django 4.2 新特性

Psycopg 3 支持

Django 现在支持 psycopg 版本 3.1.8 或更高版本。要更新您的代码,请安装 psycopg library,您不需要更改 ENGINE,因为 django.db.backends.postgresql 支持这两个库。

psycopg2 的支持可能会在将来的某个时候被弃用并移除。

请注意,psycopg 3 对 psycopg2 进行了一些不兼容的更改。因此,您可能需要进行一些更改以适应与 psycopg2 的不同之处

对列和表的注释

新的 Field.db_commentMeta.db_table_comment 选项允许分别在列和表上创建注释。例如:

  1. from django.db import models
  2. class Question(models.Model):
  3. text = models.TextField(db_comment="Poll question")
  4. pub_date = models.DateTimeField(
  5. db_comment="Date and time when the question was published",
  6. )
  7. class Meta:
  8. db_table_comment = "Poll questions"
  9. class Answer(models.Model):
  10. question = models.ForeignKey(
  11. Question,
  12. on_delete=models.CASCADE,
  13. db_comment="Reference to a question",
  14. )
  15. answer = models.TextField(db_comment="Question answer")
  16. class Meta:
  17. db_table_comment = "Question answers"

此外,新的 AlterModelTableComment 操作允许更改在 Meta.db_table_comment 中定义的表注释。

对 BREACH 攻击的缓解措施

GZipMiddleware 现在包括对 BREACH 攻击的缓解措施。它将在 gzip 响应中添加最多 100 个随机字节,以增加 BREACH 攻击的难度。在 Heal The Breach (HTB) paper 中详细了解有关缓解技术。

内存中的文件存储

新的 django.core.files.storage.InMemoryStorage 类提供了非持久性存储,对于加速测试并避免磁盘访问非常有用。

自定义文件存储

新的 STORAGES 设置允许配置多个自定义文件存储后端。它还控制了管理 文件"default" 键)和 静态文件"staticfiles" 键)的存储引擎。

在此版本中,旧的 DEFAULT_FILE_STORAGESTATICFILES_STORAGE 设置已被弃用。

次要特性

django.contrib.admin

  • 现在可以在用户界面中切换管理界面的明亮或暗色主题,并且还可以设置为跟随系统设置。
  • 管理界面现在更倾向于使用系统 UI 字体,不再需要下载字体。此外,提供了 CSS 变量,以便更轻松地覆盖默认字体系列。
  • admin/delete_confirmation.html 模板现在具有一些额外的块和脚本钩子,以便于定制。
  • filter_horizontalfilter_vertical 组件的选择选项现在是可过滤的。
  • 模板 admin/base.html 现在具有一个新的块 nav-breadcrumbs,其中包含导航标志和 breadcrumbs 块。
  • ModelAdmin.list_editable 现在在进行编辑时使用原子事务。
  • jQuery 的版本已从 3.6.0 升级到 3.6.4 。

django.contrib.auth

  • PBKDF2 密码哈希器的默认迭代次数从 390 , 000 增加到 600 , 000 。
  • UserCreationForm 现在保存了自定义用户模型的多对多表单字段。
  • 新的 BaseUserCreationForm 现在是自定义用户创建表单的推荐基类。

django.contrib.gis

  • GeoJSON 序列化程序 现在输出序列化要素的 id 键,默认为对象的主键。
  • GDALRaster 类现在支持 pathlib.Path
  • GeoIP2 类现在支持从 DB-IP 下载的 .mmdb 文件。
  • OpenLayers 模板小部件不再包含内联 CSS(也删除了以前的 map_css 块),以更好地符合严格的内容安全策略。
  • OpenLayersWidget 现在基于 OpenLayers 7.2.2(之前是 4.6.5)。
  • 新的 isempty 查找和 IsEmpty() 表达式允许在 PostGIS 上筛选空几何图形。
  • 新的 FromWKB()FromWKT() 函数允许从 Well-known binary (WKB) 和 Well-known text (WKT) 表示中创建几何图形。

django.contrib.postgres

django.contrib.sitemaps

django.contrib.staticfiles

  • ManifestStaticFilesStorage 现在具有实验性支持,在 importexport 语句中用其哈希值替换 JavaScript 模块的路径。如果你想尝试它,可以子类化 ManifestStaticFilesStorage 并将 support_js_module_import_aggregation 属性设置为 True
  • 新的 ManifestStaticFilesStorage.manifest_hash 属性提供了对清单中所有文件的哈希,并在其中任何文件更改时更改。

数据库后端

错误报告

  • 在 Python 3.11+ 上,调试页面现在显示 异常说明细粒度错误位置
  • 会话 cookie 现在被视为凭据,因此在错误报告中被隐藏并替换为星号(**********)。

表单

  • ModelForm 现在接受新的 Meta 选项 formfield_callback,以自定义表单字段。
  • modelform_factory() 现在尊重表单的 Meta 中的 formfield_callback 属性。

国际化

  • 增加了对中央库尔德语(索拉尼方言)的支持和翻译。

日志

  • django.db.backends 记录器现在以 DEBUG 级别记录事务管理查询(BEGINCOMMITROLLBACK)。

管理命令

  • makemessages 命令现在支持具有私有子标记的区域设置,例如 nl_NL-x-informal
  • 新的 makemigrations —update 选项将模型更改合并到最新的迁移中,并优化生成的操作。

迁移

  • 迁移现在支持对 enum.Flag 对象的序列化。

模型

  • QuerySet 现在在除执行聚合时的窗口函数的析取过滤查询外,广泛支持与 窗口函数 进行过滤。
  • prefetch_related() 现在支持带有切片查询集的 Prefetch 对象。
  • 现在支持在 Field 实例上 注册查找
  • 新的 robust 参数用于 on_commit(),允许在数据库事务成功提交后执行可能失败的操作。
  • 新的 KT() 表达式表示 JSONField 的键、索引或路径转换的文本值。
  • Now 现在在 MySQL 上支持微秒精度,在 SQLite 上支持毫秒精度。
  • F() 表达式输出的 BooleanField 现在可以使用 ~F() (取反操作符)进行取反。
  • Model 现在提供了一些使用数据库的方法的异步版本,使用 a 前缀:adelete()arefresh_from_db()asave()
  • Related 管理器现在提供了一些更改相关对象集合的异步方法,使用 a 前缀:aadd()aclear()aremove()aset()
  • CharField.max_length 不再需要在 PostgreSQL 上设置,因为它支持无限长度的 VARCHAR 列。

请求和响应

测试

  • test —debug-sql 选项现在使用 sqlparse 格式化 SQL 查询。

  • RequestFactoryAsyncRequestFactoryClientAsyncClient 类现在支持 headers 参数,它接受一个包含头部名称和值的字典。这允许更自然的语法来声明头部。

    1. # Before:
    2. self.client.get("/home/", HTTP_ACCEPT_LANGUAGE="fr")
    3. await self.async_client.get("/home/", ACCEPT_LANGUAGE="fr")
    4. # After:
    5. self.client.get("/home/", headers={"accept-language": "fr"})
    6. await self.async_client.get("/home/", headers={"accept-language": "fr"})

实用程序

  • django.utils.html.json_script() 函数的新参数 encoder 允许自定义 JSON 编码器类。
  • 私有的内部 vendored 版本的 urllib.parse.urlsplit() 现在会删除 '\r''\n''\t' (参见 CVE-2022-0391bpo-43882)。这是为了保护可能错误使用内部 url_has_allowed_host_and_scheme() 函数而不是使用文档中记录的处理 URL 重定向的函数的项目。Django 的函数没有受到影响。
  • 新的 django.utils.http.content_disposition_header() 函数返回根据 RFC 6266 规定的 Content-Disposition HTTP 标头值。

验证器

  • CommonPasswordValidator 使用的常见密码列表已更新为最新版本。

Django 4.2 中的不向后兼容的变更

数据库后端 API

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

  • DatabaseFeatures.allows_group_by_pk 已被移除,因为它仅保留以适应一个已被 MySQL 5.7.15 中正确的功能依赖检测所取代的 MySQL 扩展。请注意,仍然支持 DatabaseFeatures.allows_group_by_selected_pks,如果您的后端支持 GROUP BY 子句中的功能依赖检测,应该启用它,如 SQL:1999 标准所规定的。
  • inspectdb 现在使用 DatabaseIntrospection.get_table_description() 中的 display_size,而不是 internal_size 用于 CharField

不再支持 MariaDB 10.3

MariaDB 10.3 的上游支持将于 2023 年 5 月结束。 Django 4.2 支持 MariaDB 10.4 及更高版本。

不再支持 MySQL 5.7

MySQL 5.7 的上游支持将于 2023 年 10 月结束。 Django 4.2 支持 MySQL 8 及更高版本。

不再支持 PostgreSQL 11

PostgreSQL 11 的上游支持将于 2023 年 11 月结束。 Django 4.2 支持 PostgreSQL 12 及更高版本。

Model.save() 中设置 update_fields 可能现在是必需的

为了避免更新不必要的列,QuerySet.update_or_create() 现在将 update_fields 传递给 Model.save() 调用。因此,在调用 super() 之前,应该将在自定义的 save() 方法中修改的任何字段添加到 update_fields 关键字参数中。详细信息请参阅 重写之前定义的模型方法

在 MySQL 上取消了对原始聚合的支持

MySQL 8+ 允许在 GROUP BY 列上使用函数依赖,因此在 Django 4.2 之前的版本中通过对主表的主键进行分组的解决方法已被移除。因此,在 MySQL 上不再支持使用 RawSQL() 进行聚合,因为无法确定这些聚合是否在 GROUP BY 子句中需要或有效。请改用 聚合函数。

杂项

  • 未记录的 django.http.multipartparser.parse_header() 函数已被移除。请改用 django.utils.http.parse_header_parameters()
  • {% blocktranslate asvar … %} 的结果现在被标记为适用于(HTML)输出目的的安全内容。
  • 在管理界面的搜索框中移除了 autofocus HTML 属性,因为它对屏幕阅读器可能会造成困惑。
  • makemigrations —check 选项不再创建缺失的迁移文件。
  • Expression.get_group_by_cols()alias 参数已被移除。
  • sqlparse 的最低支持版本已从 0.2.2 增加到 0.3.1。
  • Exists 表达式的未记录的 negated 参数已被移除。
  • 未记录的 Query.add_annotation() 方法的 is_summary 参数已被移除。
  • 最低支持的 SQLite 版本从 3.9.0 提高到 3.21.0 。
  • asgiref 的最低支持版本已从 3.5.2 增加到 3.6.0。
  • UserCreationForm 现在拒绝仅在大小写上不同的用户名。如果需要之前的行为,请改用 BaseUserCreationForm
  • mysqlclient 的最低支持版本从 1.4.0 增加到 1.4.3。
  • argon2-cffi 的最低支持版本已从 19.1.0 增加到 19.2.0。
  • Pillow 的最低支持版本已从 6.2.0 增加到 6.2.1。
  • jinja2 的最低支持版本已从 2.9.2 增加到 2.11.0。
  • redis-py 的最低支持版本已从 3.0.0 增加到 3.4.0。
  • 手动实例化的 WSGIRequest 对象必须为 wsgi.input 提供一个类似文件的对象。之前,Django 对于 WSGI 规范中指定的预期行为更加宽松。
  • 已移除对 PROJ 版本低于 5 的支持。
  • EmailBackend 现在验证 hostnamecertificates。如果你需要以前不那么严格且不推荐的行为,可以子类化 EmailBackend 并重写 ssl_context 属性。

在 4.2 版本中弃用的功能

index_together 选项已被弃用,推荐使用 indexes

Meta.index_together 选项已被弃用,推荐使用 indexes 选项。

迁移现有的 index_together 应该作为一个迁移来处理。例如:

  1. class Author(models.Model):
  2. rank = models.IntegerField()
  3. name = models.CharField(max_length=30)
  4. class Meta:
  5. index_together = [["rank", "name"]]

应该变成:

  1. class Author(models.Model):
  2. rank = models.IntegerField()
  3. name = models.CharField(max_length=30)
  4. class Meta:
  5. indexes = [models.Index(fields=["rank", "name"])]

运行 makemigrations 命令将生成一个迁移,其中包含一个 RenameIndex 操作,用于重命名现有的索引。接下来,考虑合并迁移以从历史迁移中移除 index_together

AlterIndexTogether 迁移操作现在官方上仅支持用于 Django 4.2 之前的迁移文件。出于向后兼容性的原因,它仍然是公共 API 的一部分,没有计划弃用或移除它,但不应该在新的迁移中使用。应该使用 AddIndexRemoveIndex 操作代替。

传递编码的 JSON 字符串文字给 JSONField 已被弃用

JSONField 及其相关的查找和聚合操作曾允许传递 JSON 编码的字符串文字,这导致了在数据库后端的角度上对字符串文字是否已经编码的歧义。

在弃用期间,将尝试对字符串字面值进行 JSON 解码,并在成功时发出警告,指出应传递非编码形式的内容。

曾经用于传递 JSON 编码的字符串文字的代码:

  1. Document.objects.bulk_create(
  2. Document(data=Value("null")),
  3. Document(data=Value("[]")),
  4. Document(data=Value('"foo-bar"')),
  5. )
  6. Document.objects.annotate(
  7. JSONBAgg("field", default=Value("[]")),
  8. )

应该变成:

  1. Document.objects.bulk_create(
  2. Document(data=Value(None, JSONField())),
  3. Document(data=[]),
  4. Document(data="foo-bar"),
  5. )
  6. Document.objects.annotate(
  7. JSONBAgg("field", default=[]),
  8. )

从 Django 5.1+ 开始,字符串字面值将被隐式解释为 JSON 字符串字面值。

杂项

  • BaseUserManager.make_random_password() 方法已被弃用。请参阅 Python 的 secrets 模块的文档 了解如何使用 Python 的 secrets 模块生成密码的示例和最佳实践。

  • length_is 模板过滤器已被弃用,推荐使用 length 和在 {% if %} 标签内使用 == 运算符。例如:

    1. {% if value|length == 4 %}…{% endif %}
    2. {% if value|length == 4 %}True{% else %}False{% endif %}

    而不是:

    1. {% if value|length_is:4 %}…{% endif %}
    2. {{ value|length_is:4 }}
  • django.contrib.auth.hashers.SHA1PasswordHasherdjango.contrib.auth.hashers.UnsaltedSHA1PasswordHasherdjango.contrib.auth.hashers.UnsaltedMD5PasswordHasher 已被弃用。

  • django.contrib.postgres.fields.CICharField 已被弃用,推荐使用 CharField(db_collation="…"),并指定一个不区分大小写的非确定性排序规则(collation)。

  • django.contrib.postgres.fields.CIEmailField 已被弃用,推荐使用 EmailField(db_collation="…"),并指定一个不区分大小写的非确定性排序规则(collation)。

  • django.contrib.postgres.fields.CITextField 已被弃用,推荐使用 TextField(db_collation="…"),并指定一个不区分大小写的非确定性排序规则(collation)。

  • django.contrib.postgres.fields.CIText mixin 已被弃用。

  • BaseGeometryWidgetmap_heightmap_width 属性已被弃用,请改用 CSS 来设置地图小部件的大小。

  • SimpleTestCase.assertFormsetError() 已被弃用,推荐使用 assertFormSetError()

  • TransactionTestCase.assertQuerysetEqual() 已被弃用,推荐使用 assertQuerySetEqual()

  • 将位置参数传递给 SignerTimestampSigner 已被弃用,推荐使用仅限关键字的参数。

  • DEFAULT_FILE_STORAGE 设置已被弃用,推荐使用 STORAGES["default"]

  • STATICFILES_STORAGE 设置已被弃用,推荐使用 STORAGES["staticfiles"]

  • django.core.files.storage.get_storage_class() 函数已被弃用。