Django3.1异步视图抢先看

Django 3.1将于2020年8月发布!从3.1版本开始,Django将逐步原生支持异步,比如异步视图和中间件。

Django 3.1只支持Python 3.6、3.7、3.8以及更高版本。

下面重点介绍Django3.1的新特性:

1. 异步视图

在Django3.1中,要定义一个异步视图很简单,只需使用Python的async def语法,Django会自动探测到它们,并在异步上下文中运行它们。异步视图有很多优点,比如能够在不使用Python线程的情况下为数百个连接提供服务,允许使用慢速流、长轮询和其他的响应类型等等。

async def my_view(request):
    await asyncio.sleep(0.5)
    return HttpResponse('Hello, async world!')

无论你是运行在WSGI还是ASGI模式,都支持所有异步特性。

但是,在WSGI模式下使用异步代码可能会有性能损失,因为此时异步视图将在它们自己的一次性事件循环中运行,这意味着你虽然可以使用异步特性,如并行的异步HTTP请求,但却无法获得异步堆栈的好处。

我们可以随心所欲地混合异步和同步的视图、中间件和测试,Django会确保我们始终获得正确的执行上下文。但是,Django官方建议大多数时候依然使用同步视图,只有在真正有需求时使用异步视图,不过这完全取决于你的选择,你可以任性的都使用异步视图。

Django3.1版本中的ORM、缓存层和其他执行长时间网络调用的代码还暂时不支持异步访问,会在后期发布的版本中增加对它们的支持。(我的官网https://www.liujiangblog.com中也会持续关注并更新最新的内容和变化。)Django对异步的支持完全向后兼容,对现有的同步代码没有速度限制,它不会对任何现有的Django项目产生明显的影响。

下面是另外一个例子:

import datetime
from django.http import HttpResponse

async def current_datetime(request):
    now = datetime.datetime.now()
    html = '

欢迎访问刘江Django教程:https://www.liujiangblog.com

It is now %s.' % now return HttpResponse(html)

因为目前,Django还没有完成异步ORM的功能开发,为了在异步视图中使用ORM,需要将同步的代码转换为异步的代码,这就需要使用asgiref库,这个库已经作为安装依赖随Django一起被安装。

核心是使用asgiref.sync中的sync_to_async方法。

使用方法有两种,第一种以函数调用的方式,注意括号的位置:

from asgiref.sync import sync_to_async

results = sync_to_async(Blog.objects.get)(pk=123)

#注意圆括号,千万不要写成results = sync_to_async(Blog.objects.get(pk=123))

第二种以装饰器的方式:

from asgiref.sync import sync_to_async

@sync_to_async
def get_blog(pk):
    return Blog.objects.select_related('author').get(pk=pk)

对等的,其实也有一个异步变同步的函数,用于在同步视图中包装异步调用:

from asgiref.sync import async_to_sync

async def get_data(...):
    ...

sync_get_data = async_to_sync(get_data)

@async_to_sync
async def get_other_data(...):
    ...

2. 异步中间件

从Django3.1开始,中间件可以支持同步和异步请求的任何组合。如果Django不能同时支持这两者,它将调整请求以满足中间件的需求,但会降低性能。

默认情况下,Django假设你的中间件只能处理同步请求。要更改这个假设,请在中间件工厂函数或类上设置以下属性:

  • sync_capable: 一个布尔值,指示中间件是否可以处理同步请求。默认为True。

  • async_capable: 一个布尔值,指示中间件是否可以处理异步请求。默认为False。

如果中间件同时具有sync_capable=Trueasync_capable=True,那么Django将在不转换请求的情况下传递它。在这种情况下,可以使用asyncio.iscoroutine function()检查传递给您的get_response对象是否是一个协程函数,从而确定您的中间件是否会接收异步请求。

记住一个概念:同步和异步能力不是非左即右,互相矛盾的存在,可以共存!

那么怎么将中间件设置为同步的,或者异步的,或者同步加异步的呢?

django.utils.decorators模块中包含sync_only_middlewareasync_only_middlewaresync_and_async_middleware三个装饰器,用于帮我们实现上面的功能。

中间件返回的可调用函数必须与get_response方法的sync或async性质匹配。如果有异步get_response响应,则必须返回一个协程函数(async def)。

如果中间件提供了process_viewprocess_template_responseprocess_exception方法,则还应进行相应的调整以匹配同步/异步模式。如果你不这样做,Django会根据需要对它们进行单独的调整,并产生额外的性能惩罚。

下面是一个如何创建同时支持同步和异步功能的中间件的示例:

import asyncio
from django.utils.decorators import sync_and_async_middleware

@sync_and_async_middleware
def simple_middleware(get_response):
    # One-time configuration and initialization goes here.
    if asyncio.iscoroutinefunction(get_response):
        async def middleware(request):
            # Do something here!
            response = await get_response(request)
            return response

    else:
        def middleware(request):
            # Do something here!
            response = get_response(request)
            return response

    return middleware

总的来说,Django对同步/异步视图和同步/异步中间件之间的搭配组合有很好的适配能力,不会让我们的项目运行不起来。只不过如果搭配不当,会导致性能损失。

3. 异步测试

从Django3.1开始,具备异步测试能力。

如果您只想测试异步视图的输出,标准测试客户端将在自己的异步循环中运行它们,你不需要做任何额外的工作。

但是,如果你想为Django项目编写完全异步的测试,则需要考虑一些事情。

首先,测试方法必须是测试类上的async def方法(以便为它们提供异步上下文)。Django将自动检测任何异步测试并包装它们,以便它们在自己的事件循环中运行。

其次,如果要在异步函数中进行测试,还必须使用异步测试客户端。也就是django.test.AsyncClientself.async_client

除了不支持follow参数外,AsyncClient的使用方法和同步的测试客户端基本相同,但所有发出请求的方法都必须使用await语法:

async def test_my_thing(self):
    response = await self.async_client.get('/some-url/')
    self.assertEqual(response.status_code, 200)

4. 新增JSONField

Django3.1新增了 models.JSONFieldforms.JSONField 两种新的模型字段类型,用于保存JSON编码的数据。

MariaDB 10.2.7+、MySQL 5.7.8+、Oracle、PostgreSQL和SQLite 3.9.0+都支持JSONField。

JSONField可以自定义编码器和解码器,这从它的定义上就可以看出来:

class JSONField(encoder=None, decoder=None, **options)
  • JSONField.encoder

    可选参数。用于对诸如datetime.datetime 或者UUID之类的标准JSON序列化不了的数据指定自定义的编码器。它必须是json.JSONEncoder的子类,比如DjangoJSONEncoder

  • JSONField.decoder

    可选参数。用于解码我们自定义的编码数据。必须是 json.JSONDecoder 的子类。

如果你为JSONField字段提供了一个default默认值,它的值必须是一个不可变的类型。

对于forms.JSONField,除了同样可以自定义编码器和解码器,还有一些表单特有的性质:

  • 默认渲染的HTML元素: Textarea
  • 空值: '' (空字符串)
  • 错误信息的键: required, invalid

5. 小功能

下面例举一些相对较小的新功能。其中最主要的是,Django3.1开始使用pathlib.Path来替代传统的os.path了,建议大家还是尽快更新知识,迁移到pathlib.Path上来

django.contrib.admin

  • 新的空值过滤功能 django.contrib.admin.EmptyFieldListFilter

  • 可以清除所有的过滤操作

  • 新增可折叠的左侧边导航栏,方便我们进行菜单跳转

  • XRegExp 升级到3.2.0

  • jQuery升级到3.5.1

  • Select2 升级到4.0.13.

django.contrib.auth

  • PBKDF2密码哈希的迭代次数从180,000 提高到216,000
  • 新增PASSWORD_RESET_TIMEOUT 配置项,用于替代4.0中将废弃的 PASSWORD_RESET_TIMEOUT_DAYS
  • 密码重置将使用 SHA-256哈希算法
  • AbstractBaseUser.get_session_auth_hash() 将使用SHA-256哈希算法

django.contrib.humanize

  • intword 将支持负整数

django.contrib.sessions

  • SESSION_COOKIE_SAMESITE 现在可以接收 'None' (字符串)

django.contrib.staticfiles

  • STATICFILES_DIRS 设置项开始支持 pathlib.Path

File Storage

  • FileSystemStorage.save() 方法支持 pathlib.Path
  • FileFieldImageField 现在支持可调用的参数用于保存数据。这让你能在运行时动态选择不同的存储位置。

Migrations

  • Migrations现在也可以从没有 __init__.py 文件的目录中加载了

Models

  • 新增PositiveBigIntegerField 字段类型,类似 PositiveIntegerField ,从 09223372036854775807 都是安全的。
  • 对于外键和一对一字段的on_delete参数,现在可以接收一个RESTRICT 值,用于模拟SQL语言中的 ON DELETE RESTRICT约束行为。

更多特性请参考官方文档

更多技术文章请访问: https://www.liujiangblog.com

更多视频教程请访问: https://www.liujiangblog.com/video/

你可能感兴趣的:(Django3.1异步视图抢先看)