Django3.1異步視圖搶先看

Django 3.1將于2020年8月發(fā)布捆昏!從3.1版本開(kāi)始,Django將逐步原生支持異步镊绪,比如異步視圖和中間件寒亥。

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

下面重點(diǎn)介紹Django3.1的新特性:

1. 異步視圖

在Django3.1中,要定義一個(gè)異步視圖很簡(jiǎn)單啸罢,只需使用Python的async def語(yǔ)法编检,Django會(huì)自動(dòng)探測(cè)到它們,并在異步上下文中運(yùn)行它們扰才。異步視圖有很多優(yōu)點(diǎn)允懂,比如能夠在不使用Python線程的情況下為數(shù)百個(gè)連接提供服務(wù),允許使用慢速流衩匣、長(zhǎng)輪詢和其他的響應(yīng)類型等等蕾总。

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

無(wú)論你是運(yùn)行在WSGI還是ASGI模式,都支持所有異步特性琅捏。

但是生百,在WSGI模式下使用異步代碼可能會(huì)有性能損失,因?yàn)榇藭r(shí)異步視圖將在它們自己的一次性事件循環(huán)中運(yùn)行柄延,這意味著你雖然可以使用異步特性蚀浆,如并行的異步HTTP請(qǐng)求,但卻無(wú)法獲得異步堆棧的好處搜吧。

我們可以隨心所欲地混合異步和同步的視圖市俊、中間件和測(cè)試,Django會(huì)確保我們始終獲得正確的執(zhí)行上下文滤奈。但是摆昧,Django官方建議大多數(shù)時(shí)候依然使用同步視圖,只有在真正有需求時(shí)使用異步視圖蜒程,不過(guò)這完全取決于你的選擇绅你,你可以任性的都使用異步視圖。

Django3.1版本中的ORM搞糕、緩存層和其他執(zhí)行長(zhǎng)時(shí)間網(wǎng)絡(luò)調(diào)用的代碼還暫時(shí)不支持異步訪問(wèn)勇吊,會(huì)在后期發(fā)布的版本中增加對(duì)它們的支持曼追。(我的官網(wǎng)https://www.liujiangblog.com中也會(huì)持續(xù)關(guān)注并更新最新的內(nèi)容和變化窍仰。)Django對(duì)異步的支持完全向后兼容,對(duì)現(xiàn)有的同步代碼沒(méi)有速度限制礼殊,它不會(huì)對(duì)任何現(xiàn)有的Django項(xiàng)目產(chǎn)生明顯的影響驹吮。

下面是另外一個(gè)例子:

import datetime
from django.http import HttpResponse

async def current_datetime(request):
    now = datetime.datetime.now()
    html = '<html><body><h1>歡迎訪問(wèn)劉江Django教程:https://www.liujiangblog.com</h1>It is now %s.</body></html>' % now
    return HttpResponse(html)

因?yàn)槟壳罢胧罚珼jango還沒(méi)有完成異步ORM的功能開(kāi)發(fā),為了在異步視圖中使用ORM碟狞,需要將同步的代碼轉(zhuǎn)換為異步的代碼啄枕,這就需要使用asgiref庫(kù),這個(gè)庫(kù)已經(jīng)作為安裝依賴隨Django一起被安裝族沃。

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

使用方法有兩種,第一種以函數(shù)調(diào)用的方式脆淹,注意括號(hào)的位置:

from asgiref.sync import sync_to_async

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

#注意圓括號(hào)常空,千萬(wàn)不要寫成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)

對(duì)等的,其實(shí)也有一個(gè)異步變同步的函數(shù)盖溺,用于在同步視圖中包裝異步調(diào)用:

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開(kāi)始漓糙,中間件可以支持同步和異步請(qǐng)求的任何組合。如果Django不能同時(shí)支持這兩者烘嘱,它將調(diào)整請(qǐng)求以滿足中間件的需求昆禽,但會(huì)降低性能。

默認(rèn)情況下蝇庭,Django假設(shè)你的中間件只能處理同步請(qǐng)求醉鳖。要更改這個(gè)假設(shè),請(qǐng)?jiān)谥虚g件工廠函數(shù)或類上設(shè)置以下屬性:

  • sync_capable: 一個(gè)布爾值遗契,指示中間件是否可以處理同步請(qǐng)求辐棒。默認(rèn)為True。

  • async_capable: 一個(gè)布爾值牍蜂,指示中間件是否可以處理異步請(qǐng)求漾根。默認(rèn)為False。

如果中間件同時(shí)具有sync_capable=Trueasync_capable=True鲫竞,那么Django將在不轉(zhuǎn)換請(qǐng)求的情況下傳遞它辐怕。在這種情況下,可以使用asyncio.iscoroutine function()檢查傳遞給您的get_response對(duì)象是否是一個(gè)協(xié)程函數(shù)从绘,從而確定您的中間件是否會(huì)接收異步請(qǐng)求寄疏。

記住一個(gè)概念:同步和異步能力不是非左即右,互相矛盾的存在僵井,可以共存陕截!

那么怎么將中間件設(shè)置為同步的,或者異步的批什,或者同步加異步的呢农曲?

django.utils.decorators模塊中包含sync_only_middlewareasync_only_middlewaresync_and_async_middleware三個(gè)裝飾器,用于幫我們實(shí)現(xiàn)上面的功能乳规。

中間件返回的可調(diào)用函數(shù)必須與get_response方法的sync或async性質(zhì)匹配形葬。如果有異步get_response響應(yīng),則必須返回一個(gè)協(xié)程函數(shù)(async def)暮的。

如果中間件提供了process_view笙以、process_template_responseprocess_exception方法,則還應(yīng)進(jìn)行相應(yīng)的調(diào)整以匹配同步/異步模式冻辩。如果你不這樣做猖腕,Django會(huì)根據(jù)需要對(duì)它們進(jìn)行單獨(dú)的調(diào)整,并產(chǎn)生額外的性能懲罰恨闪。

下面是一個(gè)如何創(chuàng)建同時(shí)支持同步和異步功能的中間件的示例:

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

總的來(lái)說(shuō)谈息,Django對(duì)同步/異步視圖和同步/異步中間件之間的搭配組合有很好的適配能力,不會(huì)讓我們的項(xiàng)目運(yùn)行不起來(lái)凛剥。只不過(guò)如果搭配不當(dāng)侠仇,會(huì)導(dǎo)致性能損失。

3. 異步測(cè)試

從Django3.1開(kāi)始犁珠,具備異步測(cè)試能力逻炊。

如果您只想測(cè)試異步視圖的輸出,標(biāo)準(zhǔn)測(cè)試客戶端將在自己的異步循環(huán)中運(yùn)行它們犁享,你不需要做任何額外的工作余素。

但是,如果你想為Django項(xiàng)目編寫完全異步的測(cè)試炊昆,則需要考慮一些事情桨吊。

首先,測(cè)試方法必須是測(cè)試類上的async def方法(以便為它們提供異步上下文)凤巨。Django將自動(dòng)檢測(cè)任何異步測(cè)試并包裝它們视乐,以便它們?cè)谧约旱氖录h(huán)中運(yùn)行。

其次敢茁,如果要在異步函數(shù)中進(jìn)行測(cè)試佑淀,還必須使用異步測(cè)試客戶端。也就是django.test.AsyncClientself.async_client彰檬。

除了不支持follow參數(shù)外伸刃,AsyncClient的使用方法和同步的測(cè)試客戶端基本相同,但所有發(fā)出請(qǐng)求的方法都必須使用await語(yǔ)法:

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編碼的數(shù)據(jù)捧颅。

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

JSONField可以自定義編碼器和解碼器,這從它的定義上就可以看出來(lái):

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

    可選參數(shù)谭梗。用于對(duì)諸如datetime.datetime 或者UUID之類的標(biāo)準(zhǔn)JSON序列化不了的數(shù)據(jù)指定自定義的編碼器。它必須是json.JSONEncoder的子類宛蚓,比如DjangoJSONEncoder

  • JSONField.decoder

    可選參數(shù)激捏。用于解碼我們自定義的編碼數(shù)據(jù)。必須是 json.JSONDecoder 的子類凄吏。

如果你為JSONField字段提供了一個(gè)default默認(rèn)值远舅,它的值必須是一個(gè)不可變的類型。

對(duì)于forms.JSONField痕钢,除了同樣可以自定義編碼器和解碼器图柏,還有一些表單特有的性質(zhì):

  • 默認(rèn)渲染的HTML元素: Textarea
  • 空值: '' (空字符串)
  • 錯(cuò)誤信息的鍵: required, invalid

5. 小功能

下面例舉一些相對(duì)較小的新功能。其中最主要的是任连,Django3.1開(kāi)始使用pathlib.Path來(lái)替代傳統(tǒng)的os.path了蚤吹,建議大家還是盡快更新知識(shí),遷移到pathlib.Path上來(lái)

django.contrib.admin

  • 新的空值過(guò)濾功能 django.contrib.admin.EmptyFieldListFilter

  • 可以清除所有的過(guò)濾操作

  • 新增可折疊的左側(cè)邊導(dǎo)航欄随抠,方便我們進(jìn)行菜單跳轉(zhuǎn)

  • XRegExp 升級(jí)到3.2.0

  • jQuery升級(jí)到3.5.1

  • Select2 升級(jí)到4.0.13.

django.contrib.auth

  • PBKDF2密碼哈希的迭代次數(shù)從180,000 提高到216,000
  • 新增PASSWORD_RESET_TIMEOUT 配置項(xiàng)裁着,用于替代4.0中將廢棄的 PASSWORD_RESET_TIMEOUT_DAYS
  • 密碼重置將使用 SHA-256哈希算法
  • AbstractBaseUser.get_session_auth_hash() 將使用SHA-256哈希算法

django.contrib.humanize

  • intword 將支持負(fù)整數(shù)

django.contrib.sessions

  • SESSION_COOKIE_SAMESITE 現(xiàn)在可以接收 'None' (字符串)

django.contrib.staticfiles

  • STATICFILES_DIRS 設(shè)置項(xiàng)開(kāi)始支持 pathlib.Path

File Storage

  • FileSystemStorage.save() 方法支持 pathlib.Path
  • FileFieldImageField 現(xiàn)在支持可調(diào)用的參數(shù)用于保存數(shù)據(jù)。這讓你能在運(yùn)行時(shí)動(dòng)態(tài)選擇不同的存儲(chǔ)位置拱她。

Migrations

  • Migrations現(xiàn)在也可以從沒(méi)有 __init__.py 文件的目錄中加載了

Models

  • 新增PositiveBigIntegerField 字段類型二驰,類似 PositiveIntegerField ,從 09223372036854775807 都是安全的秉沼。
  • 對(duì)于外鍵和一對(duì)一字段的on_delete參數(shù)桶雀,現(xiàn)在可以接收一個(gè)RESTRICT 值,用于模擬SQL語(yǔ)言中的 ON DELETE RESTRICT約束行為唬复。

更多特性請(qǐng)參考官方文檔

更多技術(shù)文章請(qǐng)?jiān)L問(wèn): https://www.liujiangblog.com

更多視頻教程請(qǐng)?jiān)L問(wèn): https://www.liujiangblog.com/video/

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末矗积,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子敞咧,更是在濱河造成了極大的恐慌漠魏,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件妄均,死亡現(xiàn)場(chǎng)離奇詭異柱锹,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)丰包,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門禁熏,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人邑彪,你說(shuō)我怎么就攤上這事瞧毙。” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵宙彪,是天一觀的道長(zhǎng)矩动。 經(jīng)常有香客問(wèn)我,道長(zhǎng)释漆,這世上最難降的妖魔是什么悲没? 我笑而不...
    開(kāi)封第一講書人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮男图,結(jié)果婚禮上示姿,老公的妹妹穿的比我還像新娘。我一直安慰自己逊笆,他們只是感情好栈戳,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著难裆,像睡著了一般子檀。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上乃戈,一...
    開(kāi)封第一講書人閱讀 49,031評(píng)論 1 285
  • 那天命锄,我揣著相機(jī)與錄音,去河邊找鬼偏化。 笑死脐恩,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的侦讨。 我是一名探鬼主播驶冒,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼韵卤!你這毒婦竟也來(lái)了骗污?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤沈条,失蹤者是張志新(化名)和其女友劉穎需忿,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體蜡歹,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡屋厘,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了月而。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片汗洒。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖父款,靈堂內(nèi)的尸體忽然破棺而出溢谤,到底是詐尸還是另有隱情瞻凤,我是刑警寧澤,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布世杀,位于F島的核電站阀参,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏瞻坝。R本人自食惡果不足惜蛛壳,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望湿镀。 院中可真熱鬧,春花似錦伐憾、人聲如沸勉痴。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)蒸矛。三九已至,卻和暖如春胸嘴,著一層夾襖步出監(jiān)牢的瞬間雏掠,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工劣像, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留乡话,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓耳奕,卻偏偏與公主長(zhǎng)得像绑青,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子屋群,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345