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=True
和async_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_middleware
、async_only_middleware
和sync_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_response
和process_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.AsyncClient
或self.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.JSONField
和forms.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.0jQuery升級(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
-
FileField
和ImageField
現(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
,從0
到9223372036854775807
都是安全的秉沼。 - 對(duì)于外鍵和一對(duì)一字段的
on_delete
參數(shù)桶雀,現(xiàn)在可以接收一個(gè)RESTRICT
值,用于模擬SQL語(yǔ)言中的ON DELETE RESTRICT
約束行為唬复。