Django 2.0 版本发行说明

2017 年 12 月 2 日

欢迎来到 Django 2.0 版本!

这些发布说明涵盖了 新特性,以及从 Django 1.11 或更早版本升级时需要注意的一些 不兼容的变更。我们已经 删除了一些已经达到了弃用周期末尾的功能,并且我们已经 开始了一些功能的弃用过程

这个发布版本开始使用了 Django 的一种 松散形式的语义版本,但并没有出现与 2.0 版本发布相符的重大不兼容变更。升级应该与过去的功能发布相似的努力。

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

Python 兼容性

Django 2.0 支持 Python 3.4、3.5、3.6 和 3.7。我们 强烈建议 并且只官方支持每个系列的最新版本。

Django 1.11.x 系列是支持 Python 2.7 版本的最后一个系列。

Django 2.0 将是最后一个支持 Python 3.4 的发布系列。如果您计划在 Django 2.0 的生命周期结束后(2019 年 4 月)继续使用 Python 3.4,请改用 Django 1.11 LTS(支持到 2020 年 4 月)代替。请注意,Python 3.4 的生命周期将于 2019 年 3 月结束。

对于较旧版本的 Django 的第三方库支持

在 Django 2.0 发布之后,我们建议第三方应用程序的作者停止支持 1.11 之前的所有 Django 版本。在那个时候,你应该能够使用 python -Wd 运行你包的测试,以便弃用警告会出现。在进行弃用警告修复后,你的应用程序应该与 Django 2.0 兼容。

Django 2.0 新特性

简化 URL 路由语法

新版本 django.urls.path() 函数使用更简单易读的 URL 路由语法。一个来自 Django 旧版本的例子:

  1. url(r"^articles/(?P<year>[0-9]{4})/$", views.year_archive),

可以写成:

  1. path("articles/<int:year>/", views.year_archive),

新版本语法支持对 URL 参数的强制类型转换。在这个例子中,视图将会以数字类型接收参数 year 而不是以字符串类型。而且 URLs 的匹配限制也减少了。例如,因为参数 year 并没有像正则表达式中那样明确限制为四位数,year 为 10000 也会匹配成功。

旧版本中的 django.conf.urls.url() 函数现在可以在 django.urls.re_path() 中获取。前者依然保留,向后兼容,并没有立即弃用。旧版本中的 django.conf.urls.include() 函数现在可以从 django.urls 中导入,这样你就可以在 URLconfs 中使用 from django.urls import include, path, re_path

重新编写的文档 URL调度器 涵盖了新的语法并且提供了更多的细节。

contrib.admin 针对移动设备的优化改进

新版本的 admin 可以响应和支持所有主流移动设备。较旧的浏览器可能会遇到不同程度的功能降低。

Window 表达式

新的 Window 表达式允许在查询集上添加一个 OVER 子句。你可以在这个表达式中使用 窗口函数聚合函数

次要特性

django.contrib.admin

django.contrib.auth

  • PBKDF2 密码散列器的默认迭代计数从 36,000 增加到 100,000。

django.contrib.gis

django.contrib.postgres

  • 对于 ArrayAgg,新的 distinct 参数确定连接的值是否是不同的。
  • 新的 RandomUUID 数据库函数返回版本 4 的 UUID。它需要使用 PostgreSQL 的 pgcrypto 扩展,可以通过新的 CryptoExtension 迁移操作来激活。
  • django.contrib.postgres.indexes.GinIndex 现在支持 fastupdategin_pending_list_limit 参数。
  • 新的 GistIndex 类允许在数据库中创建 GiST 索引。新的 BtreeGistExtension 迁移操作安装了 btree_gist 扩展,以添加对不是内置的操作符类的支持。
  • inspectdb 现在可以内省 JSONField 和各种 RangeField (必须将 django.contrib.postgres 添加到 INSTALLED_APPS 中)。

django.contrib.sitemaps

  • GenericSitemap 构造函数中添加了 protocol 关键字参数。

缓存

  • cache.set_many() 现在返回一个未能插入的键的列表。对于内置的后端,失败的插入只会发生在 memcached 上。

文件存储

  • File.open() 可以作为上下文管理器使用,例如 with file.open() as f:

表单

通用视图

管理命令

  • inspectdb 现在将 MySQL 的无符号整数列翻译为 PositiveIntegerFieldPositiveSmallIntegerField
  • 新的 makemessages —add-location 选项控制 .po 文件中的注释格式。
  • loaddata 现在可以从标准输入中 读取数据
  • 新的 diffsettings —output 选项允许以统一的差异格式格式化输出。
  • 在 Oracle 上,inspectdb 现在可以内省 AutoField,如果列被创建为标识列(identity column)。
  • 在 MySQL 上,dbshell 现在支持客户端端的 TLS 证书。

迁移

模型

分页

请求和响应

模板

  • 为了增加第三方应用程序中 Engine.get_default() 的实用性,如果在 TEMPLATES 中配置了多个 DjangoTemplates 引擎,它现在将返回第一个引擎,而不是引发 ImproperlyConfigured 异常。
  • 现在自定义模板标签可以接受仅限关键字的参数。

测试

验证器

  • 新的 ProhibitNullCharactersValidatorCharField 表单字段及其子类的输入中禁止使用空字符。空字符输入是由于漏洞扫描工具而观察到的。大多数数据库会悄悄地丢弃空字符,但是当尝试将空字符保存到 PostgreSQL 的 char/text 字段时,psycopg2 2.7+ 会引发异常。

2.0 版本的不向后兼容变更

在某些地方移除了对字节字符串的支持

为了支持原生的 Python 2 字符串,旧版本的 Django 必须同时接受字节字符串和 Unicode 字符串。现在 Python 2 的支持已被移除,字节字符串应该只出现在输入/输出边界(例如处理二进制字段或 HTTP 流)附近。您可能需要更新您的代码,以将字节字符串的使用限制到最低,因为 Django 不再在某些代码路径中接受字节字符串。Python 的 -b 选项可能有助于检测代码中的这种错误。

例如,现在 reverse() 在将接收到的 argskwargs 放置在 URL 之前,使用 str() 而不是 force_text() 来强制转换它们。对于字节字符串,这会创建一个带有不希望的 b 前缀以及额外引号的字符串(str(b'foo')"b'foo'")。为了适应这种变化,在将字节字符串传递给 reverse() 之前,请调用 decode()

数据库后端 API

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

  • 现在,DatabaseOperations.datetime_cast_date_sql()datetime_cast_time_sql()datetime_trunc_sql()datetime_extract_sql()date_interval_sql() 方法仅返回执行操作的 SQL,而不再返回 SQL 和参数列表。
  • 第三方数据库后端应该添加一个名为 DatabaseWrapper.display_name 的属性,该属性包含您的后端适用于的数据库名称。Django 可能会在各种消息中使用它,例如系统检查中。
  • SchemaEditor._alter_column_type_sql() 的第一个参数现在是 model,而不再是 table
  • SchemaEditor._create_index_name() 的第一个参数现在是 table_name,而不再是 model
  • 要启用 FOR UPDATE OF 支持,请设置 DatabaseFeatures.has_select_for_update_of = True。如果数据库要求 OF 的参数是列而不是表,请设置 DatabaseFeatures.select_for_update_of_column = True
  • 要启用对 Window 表达式的支持,请将 DatabaseFeatures.supports_over_clause 设置为 True。您可能需要自定义 DatabaseOperations.window_start_rows_start_end() 和/或 window_start_range_start_end() 方法。
  • 第三方数据库后端应该添加一个名为 DatabaseOperations.cast_char_field_without_max_length 的属性,该属性指定在没有提供 max_length 参数的情况下,在 Cast 函数中将用于 CharField 的数据库数据类型。
  • DatabaseCreation._clone_test_db()get_test_db_clone_settings() 的第一个参数现在是 suffix,而不再是 number``(如果您希望为了一致性而在您的后端中重命名签名)。``django.test 现在也将这些值作为字符串而不是整数传递。
  • 第三方数据库后端应该基于 BaseDatabaseIntrospection 中的存根添加一个名为 DatabaseIntrospection.get_sequences() 的方法。

不再支持 Oracle 11.2

Oracle 11.2 的上游支持将于 2020 年 12 月结束。 Django 1.11 将在 2020 年 4 月之前得到支持,几乎达到了这个日期。 Django 2.0 正式支持 Oracle 12.1 及更高版本。

默认的 MySQL 隔离级别为读已提交(read committed)

MySQL 的默认隔离级别,可重复读(repeatable read),在典型的 Django 使用中可能会导致数据丢失。为了防止这种情况并保持与其他数据库的一致性,现在默认的隔离级别是读提交(read committed)。如果需要,您可以使用 DATABASES 设置来 使用不同的隔离级别

AbstractUser.last_namemax_length 增加到了 150。

包含了 django.contrib.auth.models.User.last_name 的迁移。如果您有一个从 AbstractUser 继承的自定义用户模型,您需要生成并应用一个数据库迁移来更新您的用户模型。

如果您想保留姓氏的 30 个字符限制,请使用自定义表单:

  1. from django.contrib.auth.forms import UserChangeForm
  2. class MyUserChangeForm(UserChangeForm):
  3. last_name = forms.CharField(max_length=30, required=False)

如果你想在管理中编辑用户时保留这个限制,请将 UserAdmin.form 设置为使用这个表单:

  1. from django.contrib.auth.admin import UserAdmin
  2. from django.contrib.auth.models import User
  3. class MyUserAdmin(UserAdmin):
  4. form = MyUserChangeForm
  5. admin.site.unregister(User)
  6. admin.site.register(User, MyUserAdmin)

在切片之后,禁止使用 QuerySet.reverse()last()

在切片后调用 QuerySet.reverse()last() 会导致意外的结果,因为切片是在重新排序后应用的。现在已禁止这样使用,例如:

  1. >>> Model.objects.all()[:2].reverse()
  2. Traceback (most recent call last):
  3. ...
  4. TypeError: Cannot reverse a query once a slice has been taken.

表单字段不再将可选参数作为位置参数接受

为了帮助防止由于表单字段参数顺序不正确而导致的运行时错误,内置表单字段的可选参数不再作为位置参数接受。例如:

  1. forms.IntegerField(25, 10)

会引发异常,并且应该替换为:

  1. forms.IntegerField(max_value=25, min_value=10)

call_command() 验证它接收到的选项

call_command() 现在验证被调用的命令的参数解析器是否定义了传递给 call_command() 的所有选项。

对于使用非 parser.add_argument() 创建的选项的自定义管理命令,请在命令上添加一个名为 stealth_options 的属性:

  1. class MyCommand(BaseCommand):
  2. stealth_options = ("option_name", ...)

索引不再接受位置参数

例子:

  1. models.Index(["headline", "-pub_date"], "index_name")

会引发异常,并且应该替换为:

  1. models.Index(fields=["headline", "-pub_date"], name="index_name")

现在在 SQLite 上启用了外键约束(Foreign key constraints)

如果尝试保存违反外键约束的现有模型实例,将会出现不兼容的后向变更(IntegrityError: FOREIGN KEY constraint failed)。

外键现在使用 DEFERRABLE INITIALLY DEFERRED 而不是 DEFERRABLE IMMEDIATE 来创建。因此,可能需要重新构建表以使用新的定义重新创建外键,特别是如果您使用了类似这样的模式:

  1. from django.db import transaction
  2. with transaction.atomic():
  3. Book.objects.create(author_id=1)
  4. Author.objects.create(id=1)

如果不将外键重新创建为 DEFERRED,那么由于现在强制执行外键约束,第一次的 create() 将会失败。

首先备份您的数据库!在升级到 Django 2.0 后,您可以使用类似以下脚本的方式重新构建表格:

  1. from django.apps import apps
  2. from django.db import connection
  3. for app in apps.get_app_configs():
  4. for model in app.get_models(include_auto_created=True):
  5. if model._meta.managed and not (model._meta.proxy or model._meta.swapped):
  6. for base in model.__bases__:
  7. if hasattr(base, "_meta"):
  8. base._meta.local_many_to_many = []
  9. model._meta.local_many_to_many = []
  10. with connection.schema_editor() as editor:
  11. editor._remake_table(model)

这个脚本尚未经过广泛测试,并且需要根据不同情况进行调整,比如多个数据库的情况。欢迎贡献改进。

另外,由于 SQLite 的表更改限制,禁止在事务中对被其他模型引用的模型或字段执行 RenameModelRenameField 操作。为了允许应用包含这些操作的迁移,您必须将 Migration.atomic 属性设置为 False

杂项

  • SessionAuthenticationMiddleware 类已被移除。自 Django 1.10 版本起,它不再提供任何功能,因为会话身份验证已无条件启用。

  • 默认的 HTTP 错误处理程序(handler404 等)现在是可调用函数,而不再是点分 Python 路径字符串。Django 更倾向于可调用引用,因为它们提供更好的性能和调试体验。

  • RedirectView 不再在 pattern_name 不存在时抑制 NoReverseMatch

  • USE_L10N 关闭时,FloatFieldDecimalField 现在在验证过程中会尊重 DECIMAL_SEPARATORTHOUSAND_SEPARATOR。例如,使用以下设置:

    1. USE_L10N = False
    2. USE_THOUSAND_SEPARATOR = True
    3. DECIMAL_SEPARATOR = ","
    4. THOUSAND_SEPARATOR = "."

    输入值 "1.345" 现在被转换为 1345,而不是 1.345

  • AbstractBaseUser 的子类不再需要实现 get_short_name()get_full_name()。(已删除引发 NotImplementedError 的基本实现。)如果实现了这些方法,django.contrib.admin 将使用它们,但不再要求它们。使用这些方法的第三方应用程序可能希望采用类似的方法。

  • FIRST_DAY_OF_WEEKNUMBER_GROUPING 格式设置现在在 JavaScript 和 JSON i18n 视图输出中保持为整数。

  • assertNumQueries() 现在忽略连接配置查询。以前,如果一个测试打开了一个新的数据库连接,那些查询可能会被包括在 assertNumQueries() 计数中。

  • Oracle 测试表空间的默认大小从 20M 增加到 50M,自动扩展的默认大小从 10M 增加到 25M 。

  • 为了提高从数据库流式传输大型结果集的性能,QuerySet.iterator() 现在一次获取 2000 行,而不是 100 行。可以使用 chunk_size 参数恢复旧的行为。例如:

    1. Book.objects.iterator(chunk_size=100)
  • JavaScriptCatalog 视图的 packages 参数中提供未知的包名称现在会引发 ValueError,而不是默默通过。

  • 现在,默认的 Model.__str__() 方法中会显示模型实例的主键,例如 Question object (1)

  • 现在,makemigrations 会检测到模型字段的 limit_choices_to 选项的更改。请将其添加到现有的迁移中,或接受为使用它的字段生成的自动生成的迁移。

  • 在 MySQL 上执行需要 自动空间转换 的查询现在会引发 NotImplementedError,而不再默默地使用未经转换的几何图形。

  • 移除了 django.core.exceptions.DjangoRuntimeWarning。它只在缓存后端中用作 CacheKeyWarning 继承 RuntimeWarning 的中间类。

  • BaseExpression._output_field 重命名为 output_field。您可能需要更新自定义表达式。

  • 在旧版本中,表单和表单集通过连接两者的 Media 来合并小部件的 Media。现在,合并尝试 保持每个列表中元素的相对顺序。如果无法保持顺序,将发出 MediaOrderConflictWarning 警告。

  • 移除了 django.contrib.gis.gdal.OGRException。自 Django 1.8 起,它一直是 GDALException 的别名。

  • 不再支持 GEOS 3.3.x 版本。

  • 选择 GeometryField 的数据的方式已更改以提高性能,在原始 SQL 查询中,现在必须将这些字段包装在 connection.ops.select 中。请参阅 GIS 教程中的 原始查询注意事项 以获取示例。

在 2.0 中被废弃的功能

Field.from_db_value()Expression.convert_value()context 参数

Field.from_db_value()Expression.convert_value()context 参数未被使用,因为它始终是一个空字典。现在这两个方法的签名是:

  1. (self, value, expression, connection)

替换成:

  1. (self, value, expression, connection, context)

对于自定义字段和表达式的旧签名,将继续支持直到 Django 3.0 版本。

杂项

  • django.db.backends.postgresql_psycopg2 模块已被弃用,推荐使用 django.db.backends.postgresql。自 Django 1.9 起,它一直是一个别名。这仅影响直接从模块导入的代码。尽管如此,DATABASES 设置仍然可以使用 'django.db.backends.postgresql_psycopg2',但您可以使用 Django 1.9 中添加的 'django.db.backends.postgresql' 名称来简化它。
  • django.shortcuts.render_to_response() 已被弃用,推荐使用 django.shortcuts.render()render() 接受与之前相同的参数,只是现在需要传递一个 request 参数。
  • DEFAULT_CONTENT_TYPE 设置已被弃用。它与第三方应用程序的兼容性不佳,并且自 HTML5 大多代替了 XHTML 后已过时。
  • HttpRequest.xreadlines() 已被弃用,建议改为迭代请求对象。
  • QuerySet.earliest()QuerySet.latest() 中的 field_name 关键字参数已被弃用,推荐将字段名称作为参数传递。请使用 .earliest('pub_date') 而不是 .earliest(field_name='pub_date')

在 2.0 版本中移除的功能

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

有关这些更改的详细信息,包括如何删除对这些特性的使用,请参阅 在 1.9 中被废弃的功能

  • django.dispatch.signals.Signal.disconnect() 中的 weak 参数已被移除。
  • django.db.backends.base.BaseDatabaseOperations.check_aggregate_support() 已被移除。
  • django.forms.extras 包已被移除。
  • assignment_tag 辅助函数已被移除。
  • SimpleTestCase.assertsRedirects() 中的 host 参数已被移除。兼容性层也被移除,该层允许在路径相同时将绝对 URL 视为与相对 URL 相等。
  • Field.relField.remote_field.to 已被移除。
  • 在模型和迁移中,ForeignKeyOneToOneFieldon_delete 参数现在是必需的。考虑合并迁移以减少需要更新的迁移数量。
  • 移除了 django.db.models.fields.add_lazy_relation()
  • 启用时间区域支持时,不再在数据库后端不支持时间区域的情况下将感知的日期时间转换为 UTC 中的非感知值,当这样的值作为参数传递给 ORM 之外的 SQL 查询执行时(例如使用 cursor.execute())。
  • django.contrib.auth.tests.utils.skipIfCustomUser() 已被移除。
  • GeoManagerGeoQuerySet 类已被移除。
  • django.contrib.gis.geoip 模块已被移除。
  • 模板加载器的 supports_recursion 检查已从以下地方移除:
    • django.template.engine.Engine.find_template()
    • django.template.loader_tags.ExtendsNode.find_template()
    • django.template.loaders.base.Loader.supports_recursion()
    • django.template.loaders.cached.Loader.supports_recursion()
  • 已移除 load_templateload_template_sources 模板加载器方法。
  • 模板加载器的 template_dirs 参数已被移除:
    • django.template.loaders.base.Loader.get_template()
    • django.template.loaders.cached.Loader.cache_key()
    • django.template.loaders.cached.Loader.get_template()
    • django.template.loaders.cached.Loader.get_template_sources()
    • django.template.loaders.filesystem.Loader.get_template_sources()
  • django.template.loaders.base.Loader.__call__() 已被移除。
  • 不接受 exception 参数的自定义错误视图的支持已被移除。
  • django.utils.feedgenerator.Atom1Feeddjango.utils.feedgenerator.RssFeedmime_type 属性已被移除。
  • include() 中的 app_name 参数已被移除。
  • 不再支持将一个包含 admin.site.urls 的 3 元组作为 include() 的第一个参数。
  • 不再支持在没有应用程序命名空间的情况下设置 URL 实例命名空间。
  • Field._get_val_from_obj() 已被移除。
  • 移除了 django.template.loaders.eggs.Loader
  • contrib.auth 函数式视图的 current_app 参数已被移除。
  • SimpleTestCase.assertRaisesMessage() 中的 callable_obj 关键字参数已被移除。
  • 不再支持在 ModelAdmin 方法上的 allow_tags 属性。
  • SyndicationFeed.add_item() 中的 enclosure 关键字参数已被移除。
  • django.template.loader.LoaderOrigindjango.template.base.StringOrigin 别名已被移除,对应的是 django.template.base.Origin

请参阅 在 1.10 中被废弃的功能 以获取有关这些更改的详细信息。

  • makemigrations --exit 选项已被移除。
  • 不再支持直接对反向外键或多对多关系进行赋值。
  • django.contrib.gis.geos.GEOSGeometryget_srid()set_srid() 方法已被移除。
  • django.contrib.gis.geos.Pointget_x(), set_x(), get_y(), set_y(), get_z(), 和 set_z() 方法已被移除。
  • django.contrib.gis.geos.Pointget_coords()set_coords() 方法已被移除。
  • django.contrib.gis.geos.MultiPolygoncascaded_union 属性已被移除。
  • django.utils.functional.allow_lazy() 已被移除。
  • shell --plain 选项已被移除。
  • django.core.urlresolvers 模块已被移除,现在请使用其新位置 django.urls
  • CommaSeparatedIntegerField 已被移除,除了对历史迁移的支持外。
  • 模板 Context.has_key() 方法已被移除。
  • 对于 django.core.files.storage.Storage.accessed_time(), created_time()modified_time() 方法的支持已被移除。
  • 当设置了 Meta.default_related_name 时,不再支持使用模型名称进行查询查找。
  • MySQL 中的 __search 查找已被移除。
  • 移除了支持自定义相关管理器类但没有 _apply_rel_filters() 方法的 shim。
  • 不再支持将 User.is_authenticated()User.is_anonymous() 作为方法使用,而不是属性。
  • Model._meta.virtual_fields 属性已被移除。
  • Field.contribute_to_class() 中的关键字参数 virtual_onlyModel._meta.add_field() 中的 virtual 已被移除。
  • javascript_catalog()json_catalog() 视图已被移除。
  • django.contrib.gis.utils.precision_wkt() 已被移除。
  • 在多表继承中,不再隐式将 OneToOneField 提升为 parent_link
  • 不再支持 Widget._format_value()
  • FileField 的方法 get_directory_name()get_filename() 已被移除。
  • mark_for_escaping() 函数以及它使用的类:EscapeDataEscapeBytesEscapeTextEscapeStringEscapeUnicode 都已被移除。
  • escape 过滤器现在使用 django.utils.html.conditional_escape()
  • Manager.use_for_related_fields 已被移除。
  • 模型的 Manager 继承遵循 MRO 继承规则。不再需要使用 Meta.manager_inheritance_from_future 来选择使用这种行为。
  • 不再支持使用 settings.MIDDLEWARE_CLASSES 的老式中间件。