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=True
和async_capable=True
,那么Django將在不轉換請求的情況下傳遞它。在這種情況下,可以使用asyncio.iscoroutine function()
檢查傳遞給您的get_response
對象是否是一個協程函數,從而確定您的中間件是否會接收異步請求。
記住一個概念:同步和異步能力不是非左即右,互相矛盾的存在,可以共存!
那么怎么將中間件設置為同步的,或者異步的,或者同步加異步的呢?
在django.utils.decorators
模塊中包含sync_only_middleware
、async_only_middleware
和sync_and_async_middleware
三個裝飾器,用於幫我們實現上面的功能。
中間件返回的可調用函數必須與get_response
方法的sync或async性質匹配。如果有異步get_response
響應,則必須返回一個協程函數(async def)。
如果中間件提供了process_view
、process_template_response
和process_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.AsyncClient
或self.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.JSONField
和forms.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
FileField
和ImageField
現在支持可調用的參數用於保存數據。這讓你能在運行時動態選擇不同的存儲位置。
Migrations
- Migrations現在也可以從沒有
__init__.py
文件的目錄中加載了
Models
- 新增
PositiveBigIntegerField
字段類型,類似PositiveIntegerField
,從0
到9223372036854775807
都是安全的。 - 對於外鍵和一對一字段的
on_delete
參數,現在可以接收一個RESTRICT
值,用於模擬SQL語言中的ON DELETE RESTRICT
約束行為。