Django 1.6 版本发行说明

备注

献给 Malcolm Tredinnick

在 2013 年 3 月 17 日,Django 项目和自由软件社区失去了一位非常亲爱的朋友和开发者。

Malcolm 是 Django 的长期贡献者,模范社区成员,一位聪明的人,也是我们的朋友。他对 Django 和其他许多开源项目的贡献几乎无法计数。 Django 核心团队中的许多人都是通过他审查第一批补丁,他的指导使我们受益匪浅。他的关心、耐心和奉献精神将永远激励着我们。

这个 Django 版本是为了纪念 Malcolm 而发布的。

-- Django 开发者们

2013 年 11 月 6 日

欢迎使用 Django 1.6 !

这些发布说明涵盖了 新功能,以及从 Django 1.5 或更早版本升级时需要注意的一些 向后不兼容的更改。我们还删除了一些功能,详细信息在 我们的弃用计划 中说明,同时我们已经 开始了一些功能的弃用过程

Python 兼容性

与 Django 1.5 一样,Django 1.6 需要 Python 2.6.5 或更高版本。Python 3 也得到官方支持。我们 强烈推荐 使用每个支持的 Python 系列的最新次要版本(2.6.X、2.7.X、3.2.X 和 3.3.X)。

Django 1.6 将是最后一个支持 Python 2.6 的发布系列;从 Django 1.7 开始,最低支持的 Python 版本将是 2.7 。

Python 3.4 目前不被支持,但将在 Django 1.7 中添加支持。

Django 1.6 的新功能有哪些?

简化了默认的项目和应用模板

startprojectstartapp 使用的默认模板已经简化和现代化。新项目默认启用 admin,不再使用 sites 框架。防止点击劫持 现在默认开启,数据库默认为 SQLite。

如果默认模板不符合您的喜好,您可以使用 自定义项目和应用程序模板

改进了事务管理

Django 的事务管理经过了彻底改进。现在默认情况下已经打开了数据库级别的自动提交。这使得事务处理更加明确,并应该提高性能。现有的 API 已被弃用,并引入了新的 API,详情请参阅 事务管理文档

引入了持久性数据库连接

Django 现在支持在多个请求中重复使用同一个数据库连接。这可以避免在每个请求开始时重新建立连接的开销。出于向后兼容性考虑,此功能默认是禁用的。请查看 持久连接 以获取详细信息。

能够发现任何测试模块中的测试

Django 1.6 提供了一个新的测试运行器,允许更灵活地定位测试。之前的运行器(django.test.simple.DjangoTestSuiteRunner)只在 INSTALLED_APPS 中的 Python 包的 models.pytests.py 模块中找到测试。

新的运行器(django.test.runner.DiscoverRunner)使用了内置于 unittest2 中的测试发现功能(Python 2.7+ 标准库中的 unittest 版本,与 Django 捆绑在一起)。使用测试发现,测试可以位于任何模块中,其名称匹配模式 test*.py

此外,提供给 ./manage.py test 的测试标签,用于指定要运行的特定测试,现在必须是完整的 Python 点路径(或目录路径),而不是伪路径 applabel.TestCase.test_method_name。这允许运行位于代码库中任何位置的测试,而不仅仅是 INSTALLED_APPS 中的测试。有关更多详细信息,请参阅 Django 中的测试

这个改变是向后不兼容的,请参阅 向后不兼容的说明

时区感知的聚合

Django 1.4 中引入的对 时区 的支持在 QuerySet.dates() 上并不完善:聚合总是在 UTC 中执行。在 Django 1.6 中解除了这个限制。使用 QuerySet.datetimes() 来对 DateTimeField 进行时区感知的聚合。

在 SQLite 中支持保存点(savepoints)

Django 1.6 在 SQLite 中添加了对保存点的支持,但存在一些 限制

BinaryField 模型字段

新的 django.db.models.BinaryField 模型字段允许在数据库中存储原始二进制数据。

GeoDjango 表单小部件

GeoDjango 现在为其地理专用字段提供了 表单字段和小部件。它们默认使用 OpenLayers,但可以自定义为使用任何其他 JavaScript 框架。

新增了 check 管理命令,用于验证兼容性。

新增了 check 管理命令,可以用来验证您当前的配置(目前是基于设置)是否与当前版本的 Django 兼容。

Model.save() 算法已更改

Model.save() 方法现在会尝试直接执行数据库 UPDATE 操作,如果实例具有主键值。以前会执行 SELECT 来确定是否需要执行 UPDATEINSERT。新算法只需要一次查询来更新现有行,而旧算法需要两次。有关更多详细信息,请参阅 Model.save()

在一些罕见的情况下,数据库在执行 UPDATE 时可能不会报告找到匹配的行。一个例子是 PostgreSQL 中的 ON UPDATE 触发器,它返回 NULL。在这种情况下,可以设置 django.db.models.Options.select_on_save 标志来强制使用旧算法进行保存。

次要特性

  • 身份验证后端可以引发 PermissionDenied 来立即终止身份验证链。
  • 可以使用 CSRF_COOKIE_HTTPONLY 来在 CSRF Cookie 上设置 HttpOnly 标志。
  • assertQuerysetEqual() 现在检查未定义的排序,并在发现未定义的排序时引发 ValueError。如果给定的 QuerySet 没有排序,并且有多个排序值要进行比较,那么排序被视为未定义。
  • 新增了 earliest(),以与 latest() 对称。
  • 除了 yearmonthday,ORM 现在还支持 hourminutesecond 查找。
  • Django 现在包装了所有 PEP 249 异常。
  • EmailFieldURLFieldIntegerFieldFloatFieldDecimalField 的默认小部件现在使用 HTML5 中可用的新类型属性(type='email'type='url'type='number')。请注意,由于当前浏览器中对本地化数字支持不稳定,因此 Django 只在数值字段未本地化时使用 number 输入类型。
  • 对于 延迟复数翻译,可以在翻译时提供 number 参数,而不是在定义时提供。
  • 对于自定义管理命令:在要求使用 BaseCommand.can_import_settings 内部选项的命令中,现在独立于处理执行命令期间应处于活动状态的区域设置的验证。后者现在可以受到新的 BaseCommand.leave_locale_alone 内部选项的影响。有关更多详细信息,请参阅 管理命令和区域设置
  • DeletionMixinsuccess_url 现在使用其 object__dict__ 进行插值。
  • HttpResponseRedirectHttpResponsePermanentRedirect 现在提供了一个 url 属性(等同于响应将重定向到的 URL)。
  • MemcachedCache 缓存后端现在使用了最新可用的 pickle 协议。
  • 新增了 SuccessMessageMixin,它为基于 FormView 的类提供了一个 success_message 属性。
  • 新增了 django.db.models.ForeignKey.db_constraintdjango.db.models.ManyToManyField.db_constraint 选项。
  • jQuery 库嵌入在管理系统中已升级至 1.9.1 版本。
  • 现在,通过新的 Feed.get_context_data() 回调,订阅源(django.contrib.syndication)可以将额外的上下文传递给订阅源模板。
  • 在 HTML 中,管理员列表列具有 column-<field_name> 类,因此可以使用 CSS 对列标题进行样式设置,例如设置列宽。
  • 在 PostgreSQL 下,可以自定义 隔离级别
  • blocktrans 模板标签现在像其他模板结构一样,尊重对于上下文中不存在的变量的 TEMPLATE_STRING_IF_INVALID
  • 在 shell 调试情况下,SimpleLazyObject 现在会提供更有帮助的表示。
  • 通用的 GeometryField 现在可以在管理员中使用 OpenLayers 小部件进行编辑。
  • 文档中包含一个 部署清单
  • diffsettings 命令增加了 --all 选项。
  • django.forms.fields.Field.__init__ 现在调用了 super(),允许字段混合类实现 __init__() 方法,并可靠地被调用。
  • BaseFormSetformset_factory() 以及 ModelForm 和相应的内联版本中添加了 validate_max 参数。对于带有 max_num 的表单集的验证行为进行了澄清。之前未记录的行为,以防止内存耗尽攻击的表单集被记录下来,同时未记录的高于 1000 或 max_num 的上限已更改,现在它总是比 max_num 多 1000。
  • 添加了 BCryptSHA256PasswordHasher 来解决 bcrypt 密码截断问题。
  • Pillow 现在是与 Django 一起使用的首选图像处理库。 PIL 已经进入弃用过程(在 Django 1.8 中将删除支持)。要升级,您应该 首先 卸载 PIL,然后 安装 Pillow。
  • ModelForm 接受了一些新的 Meta 选项。
    • 包含在 localized_fields 列表中的字段将会本地化(通过在表单字段上设置 localize)。
    • labelshelp_textserror_messages 选项可以用来自定义默认字段,请参阅 覆盖默认字段 获取详细信息。
  • 模型字段的 choices 参数现在接受一个可迭代的可迭代对象,而不是需要一个列表或元组的可迭代对象。
  • 可以使用 reason_phrase 来自定义 HTTP 响应的原因短语。
  • 在为 django.contrib.auth.views.logout()django.contrib.auth.views.password_reset()django.contrib.auth.views.password_reset_confirm()django.contrib.auth.views.password_change() 提供下一页的 URL 时,现在可以传递 URL 名称,并将其解析。
  • 新的 dumpdata --pks 选项指定要转储的对象的主键。此选项只能与一个模型一起使用。
  • 新增了 QuerySet 方法 first()last(),这些是返回与过滤条件匹配的第一个或最后一个对象的便利方法。如果没有匹配的对象,则返回 None
  • ViewRedirectView 现在支持 HTTP PATCH 方法。
  • GenericForeignKey 现在接受一个可选的 for_concrete_model 参数,当设置为 False 时,允许字段引用代理模型。默认值为 True,以保留旧的行为。
  • LocaleMiddleware 现在如果活动语言不在会话中,则会将其存储在会话中。这可以防止在会话刷新后(例如注销)丢失语言设置。
  • SuspiciousOperation 被细分为多个子类,并且每个子类都会记录到与 django.security 日志层次结构下的匹配命名的记录器中。随着这个变化,每当一个 SuspiciousOperation 到达 WSGI 处理程序以返回一个 HttpResponseBadRequest 时,将使用 handler400 机制和默认视图。
  • DoesNotExist 异常现在包含一个消息,指示用于查找的属性的名称。
  • get_or_create() 方法不再需要至少一个关键字参数。
  • SimpleTestCase 类包括一个用于测试表单集错误的新断言助手:django.test.SimpleTestCase.assertFormsetError()
  • 通过 select_related() 添加到 QuerySet 的相关字段列表可以使用 select_related(None) 清除。
  • InlineModelAdmin 上的 get_extra()get_max_num() 方法可以被覆盖,以自定义内联表单的额外和最大数量。
  • 现在,表单集具有一个 total_error_count() 方法。
  • ModelForm 字段现在可以使用 Field 构造函数的 error_messages 参数覆盖模型字段中定义的错误消息。要利用这个新特性来自定义字段,请参阅 更新的建议 来引发 ValidationError
  • ModelAdmin 在创建、编辑或删除对象后,现在会保留列表视图上的过滤器。可以通过将 preserve_filters 属性设置为 False 来恢复以前的清除过滤器的行为。
  • 新增了 FormMixin.get_prefix (默认返回 FormMixin.prefix),允许自定义表单的 prefix
  • 原始查询(Manager.raw()cursor.execute())现在可以使用 "pyformat" 参数样式,其中查询中的占位符以 '%(name)s' 形式给出,参数以字典形式传递,而不是列表(在 SQLite 上除外)。在 MySQL 和 PostgreSQL 上早就支持(但未得到官方支持),现在在 Oracle 上也可用。
  • PBKDF2 密码哈希器的默认迭代次数增加了 20%。这个向后兼容的更改不会影响现有的密码或已经子类化 django.contrib.auth.hashers.PBKDF2PasswordHasher 以更改默认值的用户。必要时,密码将被升级以使用新的迭代次数。

Django 1.6 中的向后不兼容变更

警告

除了本节中概述的更改之外,还请务必查看 弃用计划,以了解已删除的功能。如果在给定功能的弃用时间范围内未更新代码,则其删除可能会显示为不向后兼容的更改。

全新的事务管理模型

行为变更

在 Django 1.6 中,默认情况下启用了数据库级别的自动提交。虽然这不会改变 Django 事务管理的一般原则,但会引入一些不兼容性。

保存点和 assertNumQueries

事务管理的变更可能会导致额外的语句来创建、释放或回滚保存点。这在 SQLite 中更有可能发生,因为在此版本之前它不支持保存点。

如果使用 assertNumQueries() 的测试因查询次数超过预期而失败,请检查额外的查询是否与保存点相关,并相应地调整预期的查询次数。

PostgreSQL 的自动提交选项

在先前版本中,数据库级别的自动提交仅适用于 PostgreSQL,并且默认情况下被禁用。现在该选项被忽略并可以移除。

全新的测试运行器

为了与 Python 的 unittest 模块保持更一致,新的测试运行器(django.test.runner.DiscoverRunner)不会自动支持以前的运行器支持的一些测试类型:

  • 不再会查找和运行 models.pytests/__init__.py 文件中的测试。将它们移到文件名以 test 开头的文件中。
  • 不再自动发现 doctests。要将 doctests 集成到测试套件中,请遵循 Python 文档中的 建议

Django 包含了 Python 标准库中的 doctest 模块的修改版本(在 django.test._doctest 中),并包含一些额外的 doctest 工具。这些工具已被弃用,并将在 Django 1.8 中删除;doctest 测试套件应该更新以使用标准库的 doctest 模块(或转换为与 unittest 兼容的测试)。

如果希望延迟更新测试套件,可以将 TEST_RUNNER 设置为 django.test.simple.DjangoTestSuiteRunner,以完全恢复旧的测试行为。虽然 DjangoTestSuiteRunner 已被弃用,但在 Django 1.8 版本之前不会从 Django 中删除。

移除了 django.contrib.gis.tests.GeoDjangoTestSuiteRunner GeoDjango 自定义测试运行器。

这适用于开发 GeoDjango 应用程序本身及其相关开发人员,与上述有关测试运行器变更的项目相关:

django.contrib.gis.tests.GeoDjangoTestSuiteRunner 测试运行器已被移除,它实现的独立 GeoDjango 测试执行设置不再受支持。要运行 GeoDjango 测试,只需使用新的 DiscoverRunner 并指定 django.contrib.gis 应用程序。

在测试中使用自定义用户模型

新测试运行器的引入也稍微改变了测试模型的导入方式。因此,任何覆盖 AUTH_USER_MODEL 以测试与 Django 的测试用户模型之一(django.contrib.auth.tests.custom_user.CustomUserdjango.contrib.auth.tests.custom_user.ExtensionUser)行为的测试现在必须在测试模块中显式导入用户模型:

from django.contrib.auth.tests.custom_user import CustomUser


@override_settings(AUTH_USER_MODEL="auth.CustomUser")
class CustomUserFeatureTests(TestCase):
    def test_something(self):
        # Test code here
        ...

这个导入语句会强制注册自定义用户模型。如果没有这个导入语句,测试将无法切换到自定义用户模型,你将会收到一个错误报告:

ImproperlyConfigured: AUTH_USER_MODEL refers to model 'auth.CustomUser' that has not been installed

时间区域感知的 daymonthweek_day 查找

Django 1.6 在 USE_TZTrue 时引入了对 daymonthweek_day 查找的时间区域支持。以前,这些查找在不考虑当前时区的情况下都在 UTC 中执行。

这需要数据库中的 时区定义。如果使用的是 SQLite,你必须安装 pytz。如果使用的是 MySQL,你必须安装 pytz 并使用 mysql_tzinfo_to_sql 加载时区表。

添加了 QuerySet.datetimes() 方法

当 Django 1.4 中添加的 时区支持 处于活动状态时,QuerySet.dates() 查找返回了意外的结果,因为聚合是在 UTC 中执行的。为了解决这个问题,Django 1.6 引入了一个新的 API,QuerySet.datetimes()。这需要在你的代码中进行一些更改。

QuerySet.dates() 返回的是 date 对象

QuerySet.dates() 现在返回的是 date 的列表。它以前返回的是 datetime 的列表。

QuerySet.datetimes() 返回的是 datetime 的列表。

QuerySet.dates() 不再适用于 DateTimeField

当时间区域支持处于活动状态时,如果在 DateTimeField 上使用 QuerySet.dates() 会引发错误。此时应该使用 QuerySet.datetimes()

date_hierarchy 需要时间区域定义

在管理员中使用 date_hierarchy 特性时,如果它用于 DateTimeField,现在会依赖于 QuerySet.datetimes()

这需要在数据库中启用时区定义,当 USE_TZ 设置为 True 时。详细信息请参阅 了解更多

在通用视图中使用的 date_list 需要时间区域的定义

出于相同的原因,当视图基于 DateTimeFieldUSE_TZ 设置为 True 时,在日期为基础的通用视图中访问上下文中的 date_list 需要数据库中的时间区域定义。详细信息请参阅 了解更多

新的查找可能与模型字段冲突

Django 1.6 引入了在 DateTimeField 上的 hourminutesecond 查找。如果您的模型字段名为 hourminutesecond,新的查找可能会与您的字段名冲突。如果出现此问题,请附加一个明确的 exact 查找。

BooleanField 不再默认为 False

当一个 BooleanField 没有明确的 default 时,隐式的默认值是 None。在之前的 Django 版本中,默认值是 False,但这并没有准确地表示缺少值。

依赖默认值为 False 的代码在保存新的模型实例到数据库时可能会引发异常,因为 None 不是 BooleanField 的可接受值。您应该在字段定义中明确指定 default=False,或者在保存对象之前确保字段被设置为 TrueFalse

模板中的翻译和注释

提取注释后的翻译内容

使用 makemessages 命令从模板中提取可翻译文字时,现在可以正确检测到位于同一行的 {# / #} 类型的注释之后的 i18n 构造。例如:

{# A comment #}{% trans "This literal was incorrectly ignored. Not anymore" %}

翻译者注释的位置

模板内对翻译的注释 使用 {# / #} 指定的注释必须位于一行的末尾。如果不是,这些注释将被忽略,并且 makemessages 将生成警告。例如:

{# Translators: This is ignored #}{% trans "Translate me" %}
{{ title }}{# Translators: Extracted and associated with 'Welcome' below #}
<h1>{% trans "Welcome" %}</h1>

reverse() 中的引号

在反查 URL 时,Django 在将参数插入 URL 模式之前未应用 django.utils.http.urlquote。这个 bug 在 Django 1.6 中已经修复。如果你之前通过在将参数传递给 reverse() 之前应用 URL 引用来解决这个问题,这可能导致双重引用。如果发生这种情况,请从你的代码中移除 URL 引用。你还需要将在 assertRedirects() 中使用的 URL 中的特殊字符替换为它们的编码版本。

在评论应用中存储 IP 地址

评论应用现在使用 GenericIPAddressField 来存储评论者的IP地址,以支持从 IPv6 地址提交的评论。直到现在,它们是存储在 IPAddressField 中的,这个字段只支持 IPv4。当保存来自 IPv6 地址的评论时,在 MySQL 数据库上,该地址将被静默截断,并在 Oracle 上引发异常。你需要更改数据库中的列类型才能从这个更改中受益。

对于 MySQL,在您的项目数据库上执行以下查询:

ALTER TABLE django_comments MODIFY ip_address VARCHAR(39);

对于 Oracle,请执行以下查询:

ALTER TABLE DJANGO_COMMENTS MODIFY (ip_address VARCHAR2(39));

如果您不应用此更改,行为将保持不变:在 MySQL 上,IPv6 地址会被静默截断;在 Oracle 上,会生成异常。对于 SQLite 或 PostgreSQL 数据库,不需要进行数据库更改。

cursor.execute 查询中的百分比文字面值

当你通过 cursor.execute 方法运行原始 SQL 查询时,关于在查询内部加倍百分比文字面值(%)的规则已经统一。过去的行为取决于数据库后端。现在,在所有后端中,只有在你还提供替换参数的情况下,才需要加倍文字百分比字符。例如:

# No parameters, no percent doubling
cursor.execute("SELECT foo FROM bar WHERE baz = '30%'")

# Parameters passed, non-placeholders have to be doubled
cursor.execute("SELECT foo FROM bar WHERE baz = '30%%' and id = %s", [self.id])

SQLite 用户需要检查和更新这样的查询。

对于 ManyToManyField 字段的模型表单字段的帮助文本。

HTML 渲染模型表单字段,对应于用于获取硬编码句子的 ManyToManyField 模型字段:

按住 "Control" 键,或者在 Mac 上按住 "Command" 键,以选择多个选项。

(或其翻译成活动语言环境的版本)将作为帮助提示显示在它们旁边,如果用户没有指定 modelform help_text 属性(或者该字符串被附加到任何已提供的 help_text 中)。

由于这发生在模型层,因此无法阻止文本在不适用的情况下出现,例如实现了不涉及键盘和/或鼠标的用户交互的表单字段。

从 Django 1.6 开始,作为临时的向后兼容性规定,添加 "按住..." 句子的逻辑已经移动到模型表单字段层,并修改为仅在关联的小部件为 SelectMultiple 或其选定的子类时添加文本。

如果您为依赖于提供的硬编码句子的自动方式的 ManyToManyField 模型字段使用自定义模型表单字段和/或小部件,这个变更可以以向后不兼容的方式影响您。这些表单字段实现需要适应新的情况,通过提供自己的处理 help_text 属性的方式来处理它。

使用 Django 模型表单 功能以及 Django 内置的表单 字段小部件 的应用不受影响,但需要注意下面 对于 ManyToManyField 字段的模型表单字段的帮助文本的调整 中描述的内容。

QuerySet 迭代

QuerySet 的迭代方式已更改为立即将所有检索到的行转换为 Model 对象。在 Django 1.5 及之前,检索到的行是以 100 行为单位转换为 Model 对象的。

现有的代码将正常工作,但在某些用例中,转换为对象的行数可能会发生变化。这些用法包括部分遍历查询集或任何最终执行 __bool____contains__ 操作的用法。

值得注意的是,大多数数据库后端在 1.5 版本中已经一次性获取了所有行。

仍然可以通过使用 iterator() 方法来惰性地将检索到的行转换为 Model 对象。

BoundField.label_tag 现在包括表单的 label_suffix

这与 Form.as_pForm.as_ul 等方法呈现标签的方式保持一致。

如果您在模板中手动呈现 label_tag

{{ form.my_field.label_tag }}: {{ form.my_field }}

如果您在模板中手动呈现 label_tag,您会希望删除冒号(或您可能使用的其他分隔符),以避免在升级到 Django 1.6 时重复出现冒号。在 Django 1.6 中,以下模板将与 Django 1.5 中的上述模板呈现相同,唯一的区别是冒号将出现在 <label> 元素内部。

{{ form.my_field.label_tag }} {{ form.my_field }}

将呈现类似于:

<label for="id_my_field">My Field:</label> <input id="id_my_field" type="text" name="my_field" />

如果您想保留呈现不带 label_suffixlabel_tag 的当前行为,请实例化表单时使用 label_suffix=''。您还可以使用 label_tag() 上的新 label_suffix 参数来根据每个字段进行自定义。

管理员视图 _changelist_filters GET 参数

为了保留和恢复列表视图过滤器,管理员视图现在传递 _changelist_filters GET 参数。如果您有自定义的管理员模板或者您的测试依赖于以前的URL,请确保考虑到这个变化。如果您想恢复到原始行为,您可以将 preserve_filters 属性设置为 False

django.contrib.auth 密码重置使用了对 User 主键(PK)的 Base64 编码。

Django 的过去版本在密码重置视图和URLs(django.contrib.auth.views.password_reset_confirm())中使用了 User 主键的 Base 36 编码。Base 36 编码对于用户主键是整数的情况足够了,然而,随着 Django 1.5 中引入自定义用户模型,这个假设可能不再成立。

django.contrib.auth.views.password_reset_confirm() 已经修改为接受 uidb64 参数,而不是 uidb36。如果您需要反向这个视图,例如在自定义的 password_reset_email.html 模板中,请确保更新您的代码。

为了提供向后兼容性,已经添加了一个临时的 shim,用于允许在 Django 1.6 之前生成的密码重置链接继续工作;这将在 Django 1.7 中被移除。因此,只要您的站点在 Django 1.6 上运行的时间超过了 PASSWORD_RESET_TIMEOUT_DAYS,这个变化将不会产生影响。如果不是这样(例如,如果您直接从 Django 1.5 升级到 Django 1.7),那么在升级后,任何在升级到 Django 1.7 或更高版本之前生成的密码重置链接将在升级后不起作用。

此外,如果您有任何自定义密码重置的URL,您需要通过将 uidb36 替换为 uidb64 并将跟随该模式的短划线替换为斜杠来更新它们。还要将 _\- 添加到可能与 uidb64 模式匹配的字符列表中。

例子:

url(
    r"^reset/(?P<uidb36>[0-9A-Za-z]+)-(?P<token>.+)/$",
    "django.contrib.auth.views.password_reset_confirm",
    name="password_reset_confirm",
),

变成:

url(
    r"^reset/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>.+)/$",
    "django.contrib.auth.views.password_reset_confirm",
    name="password_reset_confirm",
),

您可能还希望添加 shim 来支持旧式的重置链接。使用上面的示例,您可以通过将现有的 URL 中的 django.contrib.auth.views.password_reset_confirm 替换为 django.contrib.auth.views.password_reset_confirm_uidb36,并删除 name 参数,以防止与新的 URL 冲突:

url(
    r"^reset/(?P<uidb36>[0-9A-Za-z]+)-(?P<token>.+)/$",
    "django.contrib.auth.views.password_reset_confirm_uidb36",
),

在您的应用程序在 Django 1.6 上部署了超过 PASSWORD_RESET_TIMEOUT_DAYS 后,您可以删除这个 URL 模式。

默认会话序列化方式切换为 JSON

在历史上,django.contrib.sessions 使用 pickle 来序列化会话数据,然后将其存储在后端。如果您使用 签名的 Cookie 会话后端,并且 SECRET_KEY 已知于攻击者(Django 本身没有固有的漏洞会导致其泄漏),攻击者可以在其会话中插入一个字符串,当反序列化时,会在服务器上执行任意代码。执行此操作的技术非常简单,且在互联网上很容易找到。尽管 Cookie 会话存储使用签名来防止篡改存储在 Cookie 中的数据,但如果 SECRET_KEY 泄漏,会立即升级为远程代码执行漏洞。

这种攻击可以通过使用 JSON 而不是 pickle 来序列化会话数据来减轻。为了实现这一点,Django 1.5.3 引入了一个新的设置,SESSION_SERIALIZER,用于自定义会话序列化格式。为了向后兼容,Django 1.5.3 中默认使用 pickle,但在 1.6 中我们将默认值更改为 JSON。如果您升级并从 pickle 切换到 JSON,升级之前创建的会话将丢失。虽然 JSON 序列化不支持所有像 pickle 那样的 Python 对象,但我们强烈建议使用 JSON 序列化的会话。在检查您的代码以确定 JSON 序列化是否适用于您的应用程序时,请注意以下内容:

  • JSON 要求使用字符串键,因此如果您在 request.session 中使用非字符串键,则可能会遇到问题。
  • 通过将 datetime 值传递给 set_expiry() 来设置会话到期时间将不起作用,因为 datetime 值在 JSON 中无法序列化。您可以改为使用整数值。

请查看 会话序列化 文档以获取更多详细信息。

对象关系映射器(ORM)的变更

Django 1.6 包含了许多 ORM 的变更。这些变更主要可以分为三个类别:

  1. 错误修复(例如,针对通用关系的正确连接子句、查询合并、连接提升和连接修剪的修复)
  2. 为新功能做准备。例如,ORM 现在在内部已准备好支持多列外键。
  3. 整体清理

这些变化可能会导致一些兼容性问题。例如,一些查询现在可能会生成不同的表别名。这可能会影响 QuerySet.extra()。此外,一些查询现在可能会产生不同的结果。一个例子是 exclude(condition),其中条件比较复杂(引用了 Q objects 中的多重连接)。在许多情况下,受影响的查询在 Django 1.5 中并没有生成正确的结果,但现在却能够生成正确的结果。不幸的是,还有一些情况会产生不同的结果,但无论是在 Django 1.5 还是在 1.6 中都没有生成正确的结果。

最后,ORM 的内部 API 发生了许多变化

杂项

  • django.db.models.query.EmptyQuerySet 现在不能再实例化了,它只能用作检查是否调用了 none() 的标记类:isinstance(qs.none(), EmptyQuerySet)

  • 如果您的 CSS/JavaScript 代码以前根据输入小部件的类型进行访问,您应该进行审查,因为 type='text' 的小部件现在可能会根据其对应的字段类型输出为 type='email'type='url'type='number'

  • 表单字段的 error_messages 中包含占位符的错误消息现在应始终使用具名占位符("Value '%(value)s' is too big" 而不是 "Value '%s' is too big")。有关占位符名称的详细信息,请参阅相应的字段文档。1.6 中的更改特别影响到了 DecimalFieldModelMultipleChoiceField

  • 一些针对 IntegerFieldEmailFieldIPAddressFieldGenericIPAddressFieldSlugFielderror_messages 已经被抑制,因为它们重复了与字段绑定的验证器已经提供的错误消息。

  • 由于表单验证工作流程的更改,TypedChoiceFieldcoerce 方法应该始终返回存在于 choices 字段属性中的值。这个限制应该在 Django 1.7 中被取消。

  • 在缓存后端中,超时处理方式发生了变化。显式传递 timeout=None 不再会使用默认超时。现在它将设置一个永不过期的超时。在 memcache 后端中传递 0 不再使用默认超时,而是立即设置并过期值。

  • django.contrib.flatpages 应用程序曾经为调试目的设置自定义 HTTP 标头。这个功能没有记录在文档中,而且使得缓存失效,因此已经被移除,以及之前在 django.core.xheaders 中可用的通用实现也被移除。

  • XViewMiddleware 已经从 django.middleware.doc 移动到 django.contrib.admindocs.middleware,因为它是 admindocs 的实现细节,已被证明在一般情况下不可重用。

  • GenericIPAddressField 现在只允许 blank 值,如果也允许 null 值。如果创建一个 GenericIPAddressField,允许 blank 但不允许 null,将会触发模型验证错误,因为 blank 值总是被存储为 null。以前,在一个不允许 null 的字段中存储一个 blank 值会在运行时引发数据库异常。

  • 如果在渲染模板时从方法中引发了 NoReverseMatch 异常,它将不会被消除。例如,如果 view_href() 引发了 NoReverseMatch,那么 {{ obj.view_href }} 将导致模板渲染失败。对于 {% url %} 标签没有改变,当引发 NoReverseMatch 时,它仍然会导致模板渲染失败。

  • django.test.Client.logout() 现在调用 django.contrib.auth.logout(),它将发送 user_logged_out() 信号。

  • 身份验证视图 现在通过名称反转,而不是它们在 django.contrib.auth.views 中的位置。如果您在没有 name 的情况下使用这些视图,您应该更新您的 urlpatterns 使用带有 name 参数的 django.conf.urls.url()。例如:

    (r"^reset/done/$", "django.contrib.auth.views.password_reset_complete")
    

    变成:

    url(
        r"^reset/done/$",
        "django.contrib.auth.views.password_reset_complete",
        name="password_reset_complete",
    )
    
  • RedirectView 现在有一个 pattern_name 属性,允许它通过反转 URL 来选择目标。

  • 在 Django 1.4 和 1.5 中,空字符串无意中被认为不是有效密码。这意味着 set_password() 会将空密码保存为不可用密码,就像 set_unusable_password() 一样,因此 check_password() 对于空密码始终返回 False。在此版本中已经进行了修正:空密码现在是有效的。

  • 管理员 changelist_view 以前接受一个 pop GET 参数,表示它将显示在弹出窗口中。现在已将此参数重命名为 _popup,以使其与其他管理员视图保持一致。如果您的自定义模板使用了以前的参数名称,您应该进行更新。

  • validate_email() 现在接受具有 localhost 作为域名的电子邮件地址。

  • 新的 makemessages --keep-pot 选项防止在创建 .po 文件之前删除生成的临时 .pot 文件。

  • 未记录的 django.core.servers.basehttp.WSGIServerException 已被移除。请改用标准库提供的 socket.error。这个变化也在 Django 1.5.5 中发布过。

  • django.views.generic.base.RedirectView.get_redirect_url() 的签名已更改,现在也接受位置参数(*args, **kwargs)。任何未命名的捕获组现在都将传递给 get_redirect_url(),如果您不更新自定义方法的签名,可能会导致 TypeError

在 1.6 中被废弃的功能

事务管理的 API

Django 1.6 对事务管理进行了完全改进,并且当前的 API 已被弃用:

  • django.middleware.transaction.TransactionMiddleware
  • django.db.transaction.autocommit
  • django.db.transaction.commit_on_success
  • django.db.transaction.commit_manually
  • TRANSACTIONS_MANAGED 设置

django.contrib.comments

Django 的评论框架已被弃用,不再受支持。它将在 Django 1.6 和 1.7 中可用,并在 Django 1.8 中移除。大多数用户可以通过自定义解决方案或使用类似 Disqus 的托管产品来更好地满足其需求。

以前被称为 django.contrib.comments 的代码仍然可以在一个外部存储库中获得。

不支持早于 8.4 版本的 PostgreSQL 。

在 PostgreSQL 8.2 的上游支持期限于 2011 年 12 月结束,而 8.3 的上游支持期限于 2013 年 2 月结束。因此,Django 1.6 将 8.4 版本设置为它正式支持的最低 PostgreSQL 版本。

强烈建议您使用最新版本的可用的 PostgreSQL,以利用性能改进和 PostgreSQL 9.x 中可用的原生流复制功能。

cyclefirstof 的更改

模板系统通常会对所有变量进行转义,以防止 XSS 攻击。然而,由于历史原因,cyclefirstof 标签会原样呈现其参数。

Django 1.6 开始了一个过程来纠正这个不一致性。future 模板库提供了 cyclefirstof 的替代实现,它们会自动转义其输入。如果您使用这些标签,建议在模板顶部包含以下行以启用新的行为:

{% load cycle from future %}

或:

{% load firstof from future %}

实现旧行为的标签已被弃用,并且在 Django 1.8 中,旧行为将被新行为替代。为了确保与未来版本的 Django 兼容,现有模板应该被修改以使用 future 版本。

如有必要,您可以使用 mark_safe(){% autoescape off %} 来暂时禁用自动转义。

CACHE_MIDDLEWARE_ANONYMOUS_ONLY 设置

CacheMiddlewareUpdateCacheMiddleware 曾经提供了一种只有在请求不是由已登录用户发起时才缓存的方法。这个机制在很大程度上是无效的,因为中间件正确地考虑了 Vary: Cookie HTTP 头,而这个头在各种情况下都被设置,比如:

  • 访问会话,或者
  • 使用默认启用的 CSRF 保护,或者
  • 使用客户端库设置了 cookies,比如 `Google Analytics`__

这使得缓存在每个会话中都能有效地工作,而不受 CACHE_MIDDLEWARE_ANONYMOUS_ONLY 设置的影响。

对于小部件上的 _has_changed 方法

如果您定义了自己的表单小部件并在小部件上定义了 _has_changed 方法,现在应该在表单字段本身上定义这个方法。

module_name 模型 _meta 属性

Model._meta.module_name 已更名为 model_name。尽管它是一个私有 API,但它将经历常规的弃用过程。

get_(add|change|delete)_permission 模型 _meta 方法

Model._meta.get_(add|change|delete)_permission 方法已被弃用。即使它们不是公共 API 的一部分,它们也将经历常规的弃用过程。您可以将它们替换为 django.contrib.auth.get_permission_codename('action', Model._meta),其中 'action''add''change''delete'

get_query_set 和类似的方法已重命名为 get_queryset

返回一个 QuerySet 的方法,如 Manager.get_query_setModelAdmin.queryset,已被重命名为 get_queryset

如果您正在编写一个库,其中实现了一个例如 Manager.get_query_set 方法,并且需要支持旧版本的 Django,您应该重命名该方法并有条件地添加一个具有旧名称的别名:

class CustomManager(models.Manager):
    def get_queryset(self):
        pass  # ...

    if django.VERSION < (1, 6):
        get_query_set = get_queryset

    # For Django >= 1.6, models.Manager provides a get_query_set fallback
    # that emits a warning when used.

如果您正在编写一个需要调用 get_queryset 方法并且必须支持旧版 Django 的库,您应该编写:

get_queryset = (
    some_manager.get_query_set
    if hasattr(some_manager, "get_query_set")
    else some_manager.get_queryset
)
return get_queryset()  # etc

对于一般情况下的自定义管理器,既实现了自己的 get_queryset 方法又调用该方法,并且需要与较旧版本的 Django 以及尚未更新的库一起使用,定义一个如下的 get_queryset_compat 方法并在管理器内部使用它是有用的:

class YourCustomManager(models.Manager):
    def get_queryset(self):
        return YourCustomQuerySet()  # for example

    if django.VERSION < (1, 6):
        get_query_set = get_queryset

    def active(self):  # for example
        return self.get_queryset_compat().filter(active=True)

    def get_queryset_compat(self):
        get_queryset = (
            self.get_query_set if hasattr(self, "get_query_set") else self.get_queryset
        )
        return get_queryset()

这有助于最小化所需的更改,同时在子类(例如 Django 1.5 中的 RelatedManagers)可能覆盖 get_query_setget_queryset 的情况下也能正常工作。

shortcut 视图和 URL 配置

shortcut 视图在 1.0 版本发布后不久从 django.views.defaults 移动到了 django.contrib.contenttypes.views,但旧位置从未弃用。这个疏忽在 Django 1.6 中得到了纠正,现在应该使用新的位置。

URL 配置中的 django.conf.urls.shortcut 也已被弃用。如果您在 URL 配置中包含它,只需替换为:

(r"^prefix/", include("django.conf.urls.shortcut")),

使用:

(
    r"^prefix/(?P<content_type_id>\d+)/(?P<object_id>.*)/$",
    "django.contrib.contenttypes.views.shortcut",
),

没有指定 fieldsexcludeModelForm

以前,如果您想要一个 ModelForm 使用模型上的所有字段,您可以简单地省略 Meta.fields 属性,所有字段都会被使用。

这可能会导致安全问题,其中字段被添加到模型,并且无意中自动变为可由最终用户编辑。在某些情况下,特别是布尔字段的情况下,这个问题可能是完全不可见的。这是一种 批量赋值漏洞

因此,这种行为已被弃用,并强烈不建议使用 Meta.exclude 选项。相反,应该将所有打算包含在表单中的字段都明确列在 fields 属性中。

如果在您的情况下确实不适用这个安全问题,那么有一种快捷方式可以明确指示使用所有字段 - 使用字段属性的特殊值 "__all__"

class MyModelForm(ModelForm):
    class Meta:
        fields = "__all__"
        model = MyModel

如果您有自定义的 ModelForm,只需要在管理界面中使用,还有另一种选择。管理界面有自己定义字段的方法(fieldsets 等),因此在 ModelForm 中添加字段列表是多余的。相反,只需省略 ModelFormMeta 内部类,或省略 Meta.model 属性。由于 ModelAdmin 子类知道它是哪个模型,它可以添加必要的属性来派生一个可用的 ModelForm。这种行为也适用于较早的 Django 版本。

没有显式字段的 UpdateViewCreateView

通用视图 CreateViewUpdateView,以及从 ModelFormMixin 派生的任何其他视图,都容易受到上面描述的安全问题的影响,因为它们可以自动创建一个使用模型的所有字段的 ModelForm

因此,如果您使用这些视图来编辑模型,您必须提供 fields 属性(在 Django 1.6 中新增),它是一个模型字段的列表,工作方式与 ModelFormMeta.fields 属性相同。或者,您可以将 form_class 属性设置为明确定义要使用的字段的 ModelForm。已经弃用了定义用于与模型一起使用但没有显式字段列表的 UpdateViewCreateView 子类。

对于 ManyToManyField 字段的模型表单字段的帮助文本的调整

所有标准模型字段或模型表单字段对 ManyToManyField 模型字段的 help_text 属性执行的特殊处理,如上述 对于 ManyToManyField 字段的模型表单字段的帮助文本。 中描述的,已经弃用,并将在 Django 1.8 中移除。

这些字段的帮助文本需要由应用程序、自定义表单字段或小部件处理,就像其他模型字段类型一样。

Back to Top