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 = '<html><body><h1>歡迎訪問劉江Django教程:https://www.liujiangblog.com</h1>It is now %s.</body></html>' % 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/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM