Django 2.0 发行说明 — Django 文档
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。 我们【X3X】强烈推荐【X23X】,官方只支持各系列的最新版本。
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 发布之后,我们建议第三方应用程序作者放弃对 Django 1.11 之前的所有版本的支持。 那时,您应该能够使用 python -Wd
运行包的测试,以便出现弃用警告。 修复弃用警告后,您的应用程序应该与 Django 2.0 兼容。
Django 2.0 中的新功能
简化的 URL 路由语法
新的 django.urls.path() 函数允许更简单、更易读的 URL 路由语法。 例如,以前的 Django 版本中的这个例子:
url(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive),
可以写成:
path('articles/<int:year>/', views.year_archive),
新语法支持 URL 参数的类型强制。 在示例中,视图将接收 year
关键字参数作为整数而不是字符串。 此外,在重写的示例中,将匹配的 URL 受到的限制略少。 例如,年份 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
管理员现在可以响应并支持所有主要的移动设备。 较旧的浏览器可能会遇到不同程度的优雅降级。
小功能
django.contrib.admin
- 新的 ModelAdmin.autocomplete_fields 属性和 ModelAdmin.get_autocomplete_fields() 方法允许使用 Select2 搜索小部件
ForeignKey
和 [X172X][X18X] ]。
django.contrib.auth
- PBKDF2 密码哈希器的默认迭代计数从 36,000 增加到 100,000。
django.contrib.gis
- 添加 MySQL 支持 AsGeoJSON 函数、GeoHash 函数、IsValid 函数、:lookup:`isvalid` 查找和 距离查找。
- 添加了 Azimuth 和 LineLocatePoint 函数,支持 PostGIS 和 SpatiaLite。
- 任何从 GeoJSON 导入的 GEOSGeometry 现在都有其 SRID 集。
- 添加了 OSMWidget.default_zoom 属性来自定义地图的默认缩放级别。
- 通过 metadata、info 和 metadata 属性使元数据在栅格上可读和可编辑。
- 允许使用
papsz_options
将特定于驱动程序的创建选项传递给 GDALRaster 对象。 - 允许在 GDAL 的内部虚拟文件系统中创建 GDALRaster 对象。 现在可以从内存中 创建栅格并将其转换为二进制数据 。
- 新的 GDALBand.color_interp() 方法返回波段的颜色解释。
django.contrib.postgres
- ArrayAgg 的新
distinct
参数确定连接的值是否不同。 - 新的 RandomUUID 数据库函数返回版本 4 UUID。 它需要使用 PostgreSQL 的
pgcrypto
扩展,可以使用新的 CryptoExtension 迁移操作激活该扩展。 - django.contrib.postgres.indexes.GinIndex 现在支持
fastupdate
和gin_pending_list_limit
参数。 - 新的 GistIndex 类允许在数据库中创建
GiST
索引。 新的 BtreeGistExtension 迁移操作安装了btree_gist
扩展以添加对非内置运算符类的支持。 - :djadmin:`inspectdb` 现在可以自省
JSONField
和各种RangeField
(django.contrib.postgres
必须在INSTALLED_APPS
中)。
缓存
cache.set_many()
现在返回未能插入的密钥列表。 对于内置后端,插入失败只能发生在 memcached 上。
表格
- SplitDateTimeWidget 和 SplitHiddenDateTimeWidget 的新
date_attrs
和time_attrs
参数允许为DateInput
和 [ X183X](或隐藏的)子部件。 - 新的 Form.errors.get_json_data() 方法将表单错误作为适合包含在 JSON 响应中的字典返回。
管理命令
- :djadmin:`inspectdb` 现在将 MySQL 的无符号整数列转换为
PositiveIntegerField
或PositiveSmallIntegerField
。 - 新的
makemessages --add-location
选项控制 PO 文件中的注释格式。 - :djadmin:`loaddata` 现在可以 从 stdin 读取。
- 新的
diffsettings --output
选项允许以统一的差异格式格式化输出。 - 在 Oracle 上,如果将列创建为标识列,:djadmin:`inspectdb` 现在可以自省
AutoField
。 - 在 MySQL 上,:djadmin:`dbshell` 现在支持客户端 TLS 证书。
迁移
- 新的
squashmigrations --squashed-name
选项允许命名压缩的迁移。
型号
- 新的 StrIndex 数据库函数在另一个字符串中查找字符串的起始索引。
- 在 Oracle 上,
AutoField
和BigAutoField
现在被创建为 标识列 。 - QuerySet.iterator() 的新
chunk_size
参数控制 Python 数据库客户端在从数据库流式传输结果时获取的行数。 对于不支持服务器端游标的数据库,它控制 Django 从数据库适配器获取的结果数量。 - QuerySet.earliest()、QuerySet.latest() 和 Meta.get_latest_by 现在允许按多个字段排序。
- 新增 ExtractQuarter 函数从 DateField 和 DateTimeField 中提取季度,并通过 :lookup:`quarter` 查找公开。
- 添加了 TruncQuarter 函数以将 DateField 和 DateTimeField 截断到季度的第一天。
- 向基于类的索引添加了 db_tablespace 参数。
- 如果数据库支持本机持续时间字段(Oracle 和 PostgreSQL),Extract 现在可以与 DurationField 一起使用。
- 向 QuerySet.select_for_update() 添加了
of
参数,支持 PostgreSQL 和 Oracle,以仅锁定特定表中的行而不是所有选定表。 当 select_for_update() 与 select_related() 结合使用时,它可能特别有用。 - QuerySet.in_bulk() 的新
field_name
参数允许基于任何唯一模型字段获取结果。 - CursorWrapper.callproc() 现在采用可选的关键字参数字典,如果后端支持此功能。 在 Django 的内置后端中,只有 Oracle 支持。
- 新的 connection.execute_wrapper() 方法允许 围绕执行数据库查询 安装包装器。
- 内置聚合的新
filter
参数允许 将不同的条件 添加到相同字段或关系的多个聚合。 - 添加了对 Meta.ordering 中的表达式的支持。
- QuerySet.values_list() 的新
named
参数允许以命名元组的形式获取结果。 - 新的 FilteredRelation 类允许向查询集添加
ON
子句。
模板
- 为了增加 Engine.get_default() 在第三方应用程序中的实用性,如果在
TEMPLATES
中配置多个DjangoTemplates
引擎,它现在返回第一个引擎,而不是提高 [ X191X]。 - 自定义模板标签现在可以接受仅关键字参数。
测试
- 为 LiveServerTestCase 添加了线程支持。
- 添加了允许为 Oracle 自定义测试表空间参数的设置::setting:`DATAFILE_SIZE`, :setting:`DATAFILE_TMP_SIZE`, :setting:`DATAFILE_EXTSIZE` , 和 :setting:`DATAFILE_TMP_EXTSIZE`。
验证器
- 新的 ProhibitNullCharactersValidator 不允许在 CharField 表单字段及其子类的输入中使用空字符。 从漏洞扫描工具中观察到空字符输入。 大多数数据库会默默地丢弃空字符,但 psycopg2 2.7+ 在尝试使用 PostgreSQL 将空字符保存到字符/文本字段时引发异常。
2.0 中向后不兼容的变化
在某些地方删除了对字节串的支持
为了支持原生 Python 2 字符串,较旧的 Django 版本必须同时接受字节字符串和 Unicode 字符串。 既然 Python 2 支持已被删除,字节串只能在输入/输出边界附近遇到(例如,处理二进制字段或 HTTP 流)。 您可能需要更新代码以将字节串的使用限制在最低限度,因为 Django 不再接受某些代码路径中的字节串。 Python 的 -b
选项可能有助于检测代码中的错误。
例如,reverse()
现在使用 str()
而不是 force_text()
来强制它接收到的 args
和 kwargs
,然后再将它们放入网址。 对于字节串,这会创建一个带有不需要的 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
属性,其数据库数据类型将在 Cast 函数中用于CharField
,如果max_length
未提供参数。 DatabaseCreation._clone_test_db()
和get_test_db_clone_settings()
的第一个参数现在是suffix
而不是number
(以防您想重命名后端中的签名以保持一致性)。django.test
现在也将这些值作为字符串而不是整数传递。- 第三方数据库后端应基于
BaseDatabaseIntrospection
中的存根添加DatabaseIntrospection.get_sequences()
方法。
不再支持 Oracle 11.2
Oracle 11.2 上游支持的结束时间是 12 月。 2020. Django 1.11 将得到支持,直到 2020 年 4 月,几乎达到这个日期。 Django 2.0 正式支持 Oracle 12.1+。
默认 MySQL 隔离级别是已提交读
MySQL 的默认隔离级别,可重复读取,在典型的 Django 使用中可能会导致数据丢失。 为了防止这种情况并与其他数据库保持一致,默认隔离级别现在是读提交。 如果需要,您可以使用 :setting:`DATABASES` 设置来 使用不同的隔离级别 。
AbstractUser.last_name max_length increased to 150
包括 django.contrib.auth.models.User.last_name 的迁移。 如果您有一个从 AbstractUser
继承的自定义用户模型,则需要为您的用户模型生成并应用数据库迁移。
如果您想保留姓氏 30 个字符的限制,请使用自定义表单:
from django.contrib.auth.forms import UserChangeForm
class MyUserChangeForm(UserChangeForm):
last_name = forms.CharField(max_length=30, required=False)
如果您希望在编辑用户时在 admin 中保留此限制,请设置 UserAdmin.form
以使用此表单:
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.models import User
class MyUserAdmin(UserAdmin):
form = MyUserChangeForm
admin.site.unregister(User)
admin.site.register(User, MyUserAdmin)
切片后禁止QuerySet.reverse()和last()
由于在重新排序后应用了切片,因此在切片查询集上调用 QuerySet.reverse()
或 last()
会导致意外结果。 现在禁止这样做,例如:
>>> Model.objects.all()[:2].reverse()
Traceback (most recent call last):
...
TypeError: Cannot reverse a query once a slice has been taken.
表单字段不再接受可选参数作为位置参数
为了帮助防止由于表单字段参数的顺序不正确而导致的运行时错误,内置表单字段的可选参数不再被接受为位置参数。 例如:
forms.IntegerField(25, 10)
引发异常,应替换为:
forms.IntegerField(max_value=25, min_value=10)
call_command() 验证它收到的选项
call_command()
现在验证被调用命令的参数解析器定义了所有传递给 call_command()
的选项。
对于使用不是使用 parser.add_argument()
创建的选项的自定义管理命令,在命令上添加 stealth_options
属性:
class MyCommand(BaseCommand):
stealth_options = ('option_name', ...)
索引不再接受位置参数
例如:
models.Index(['headline', '-pub_date'], 'index_name')
引发异常,应替换为:
models.Index(fields=['headline', '-pub_date'], name='index_name')
现在在 SQLite 上启用了外键约束
如果尝试保存违反外键约束的现有模型实例,这将显示为向后不兼容的更改 (IntegrityError: FOREIGN KEY constraint failed
)。
现在使用 DEFERRABLE INITIALLY DEFERRED
而不是 DEFERRABLE IMMEDIATE
创建外键。 因此,可能需要重建表以使用新定义重新创建外键,特别是如果您使用这样的模式:
from django.db import transaction
with transaction.atomic():
Book.objects.create(author_id=1)
Author.objects.create(id=1)
如果您不将外键重新创建为 DEFERRED
,那么由于强制执行外键约束,第一个 create()
将失败。
先备份你的数据库! 升级到 Django 2.0 后,您可以使用类似于此的脚本重建表:
from django.apps import apps
from django.db import connection
for app in apps.get_app_configs():
for model in app.get_models(include_auto_created=True):
if model._meta.managed and not (model._meta.proxy or model._meta.swapped):
for base in model.__bases__:
if hasattr(base, '_meta'):
base._meta.local_many_to_many = []
model._meta.local_many_to_many = []
with connection.schema_editor() as editor:
editor._remake_table(model)
这个脚本没有经过广泛的测试,需要适应多种情况,比如多数据库。 随意贡献改进。
另外,由于SQLite的换表限制,禁止对事务中其他模型引用的模型或字段进行RenameModel和RenameField操作。 为了允许应用包含这些操作的迁移,您必须将 Migration.atomic
属性设置为 False
。
杂项
SessionAuthenticationMiddleware
类被删除。 它没有提供任何功能,因为 Django 1.10 中无条件启用了会话身份验证。默认的 HTTP 错误处理程序(
handler404
等)现在是可调用的,而不是带点的 Python 路径字符串。 Django 偏爱可调用引用,因为它们提供了更好的性能和调试体验。如果
pattern_name
不存在,RedirectView 不再使NoReverseMatch
静音。当 :setting:`USE_L10N` 关闭时,FloatField 和 DecimalField 现在尊重 :setting:`DECIMAL_SEPARATOR` 和 :设置:验证期间`THOUSAND_SEPARATOR`。 例如,使用设置:
USE_L10N = False USE_THOUSAND_SEPARATOR = True DECIMAL_SEPARATOR = ',' THOUSAND_SEPARATOR = '.'
"1.345"
的输入现在转换为1345
而不是1.345
。AbstractBaseUser 的子类不再需要实现
get_short_name()
和get_full_name()
。 (引发NotImplementedError
的基本实现被删除。)django.contrib.admin
使用这些方法(如果实现但不需要它们)。 使用这些方法的第三方应用程序可能希望采用类似的方法。FIRST_DAY_OF_WEEK
和NUMBER_GROUPING
格式设置现在在 JavaScript 和 JSON i18n 视图输出中保留为整数。assertNumQueries() 现在忽略连接配置查询。 以前,如果测试打开了一个新的数据库连接,这些查询可以作为
assertNumQueries()
计数的一部分包含在内。Oracle 测试表空间的默认大小从 20M 增加到 50M,默认自动扩展大小从 10M 增加到 25M。
为了提高从数据库流式传输大型结果集时的性能,QuerySet.iterator() 现在一次获取 2000 行而不是 100 行。 可以使用
chunk_size
参数恢复旧行为。 例如: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
参数未使用,因为它始终是一个空字典。 这两种方法的签名现在是:
(self, value, expression, connection)
代替:
(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.rel
和Field.remote_field.to
被移除。ForeignKey
和OneToOneField
的on_delete
参数现在在模型和迁移中是必需的。 考虑压缩迁移,以便更新更少的迁移。django.db.models.fields.add_lazy_relation()
被移除。- 当启用时区支持时,当这些值作为参数传递给在 ORM 之外执行的 SQL 查询时,不支持时区的数据库后端不再将感知日期时间转换为 UTC 中的原始值,例如
cursor.execute()
。 django.contrib.auth.tests.utils.skipIfCustomUser()
被移除。GeoManager
和GeoQuerySet
类被删除。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_template
和load_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.Atom1Feed
和django.utils.feedgenerator.RssFeed
的mime_type
属性被移除。include()
的app_name
参数被删除。- 删除了将 3 元组(包括
admin.site.urls
)作为第一个参数传递给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.base.Origin
的django.template.loader.LoaderOrigin
和django.template.base.StringOrigin
别名被删除。
有关这些更改的详细信息,请参阅 1.10 中弃用的 功能。
makemigrations --exit
选项被删除。- 删除了对直接分配给反向外键或多对多关系的支持。
- 删除了
django.contrib.gis.geos.GEOSGeometry
的get_srid()
和set_srid()
方法。 get_x()
、set_x()
、get_y()
、set_y()
、get_z()
和set_z()
方法django.contrib.gis.geos.Point
] 被删除。- 删除了
django.contrib.gis.geos.Point
的get_coords()
和set_coords()
方法。 django.contrib.gis.geos.MultiPolygon
的cascaded_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()
方法的自定义相关管理器类的垫片。 - 不再支持使用
User.is_authenticated()
和User.is_anonymous()
作为方法而不是属性。 Model._meta.virtual_fields
属性被移除。- 删除了
Field.contribute_to_class()
中的关键字参数virtual_only
和Model._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()
函数及其使用的类:EscapeData
、EscapeBytes
、EscapeText
、EscapeString
和EscapeUnicode
移除。escape
过滤器现在使用django.utils.html.conditional_escape()
。Manager.use_for_related_fields
被移除。- 模型
Manager
继承遵循MRO继承规则。 取消了使用Meta.manager_inheritance_from_future
选择加入该行为的要求。 - 删除了对使用
settings.MIDDLEWARE_CLASSES
的旧式中间件的支持。