鍍金池/ 教程/ Python/ 按需內(nèi)容處理
點(diǎn)擊劫持保護(hù)
安全問題歸檔
Model 類參考
將遺留數(shù)據(jù)庫整合到Django
關(guān)聯(lián)對象參考
內(nèi)建基于類的視圖的API
聚合
Django 中的用戶認(rèn)證
django.contrib.humanize
Django管理文檔生成器
分頁
使用Django輸出CSV
加密簽名
文件儲存API
安全
Django中的測試
國際化和本地化
為Django編寫首個補(bǔ)丁
條件表達(dá)式
日志
模型元選項
部署靜態(tài)文件
執(zhí)行查詢
使用Django認(rèn)證系統(tǒng)
基于類的視圖
中間件
編寫自定義的django-admin命令
Django 的設(shè)置
格式本地化
數(shù)據(jù)庫訪問優(yōu)化
錯誤報告
基于類的內(nèi)建通用視圖
編寫自定義存儲系統(tǒng)
編寫你的第一個 Django 程序 第3部分
編寫數(shù)據(jù)庫遷移
使用表單
編寫你的第一個 Django 程序 第2部分
編寫你的第一個 Django 程序 第1部分
如何使用會話
系統(tǒng)檢查框架
新手入門
信號
編寫視圖
如何使用WSGI 部署
編寫你的第一個Django應(yīng)用,第6部分
常見的網(wǎng)站應(yīng)用工具
Widgets
內(nèi)建的視圖
模型實(shí)例參考
視圖層
Django中的密碼管理
高級教程:如何編寫可重用的應(yīng)用
國際化和本地化
"本地特色"附加功能
TemplateResponse 和 SimpleTemplateResponse
模式編輯器
文件上傳
快速安裝指南
部署 Django
表單 API
表單素材 ( <code>Media</code> 類)
管理文件
其它核心功能
查找 API 參考
表單
Admin
數(shù)據(jù)庫函數(shù)
自定義查找
使用基于類的視圖處理表單
管理操作
開發(fā)過程
編寫你的第一個Django應(yīng)用,第5部分
進(jìn)行原始的sql查詢
模型層
多數(shù)據(jù)庫
編寫你的第一個 Django 程序 第4部分
Django安全
Django 初探
Django異常
重定向應(yīng)用
按需內(nèi)容處理
管理器
視圖裝飾器
驗證器
使用Django輸出PDF
File對象
Django 的快捷函數(shù)
基于類的通用視圖 —— 索引
為模型提供初始數(shù)據(jù)
模板層
URL調(diào)度器
中間件
模型

按需內(nèi)容處理

HTTP客戶端可能發(fā)送一些協(xié)議頭來告訴服務(wù)端它們已經(jīng)看過了哪些資源。這在獲取網(wǎng)頁(使用HTTPGET請求)時非常常見,可以避免發(fā)送客戶端已經(jīng)獲得的完整數(shù)據(jù)。然而,相同的協(xié)議頭可用于所有HTTP方法(POST, PUT, DELETE, 以及其它)。

對于每一個Django從視圖發(fā)回的頁面(響應(yīng)),都會提供兩個HTTP協(xié)議頭:ETagLast-Modified。這些協(xié)議頭在HTTP響應(yīng)中是可選的。它們可以由你的視圖函數(shù)設(shè)置,或者你可以依靠 CommonMiddleware 中間件來設(shè)置ETag 協(xié)議頭。

當(dāng)你的客戶端再次請求相同的資源時,它可能會發(fā)送 If-modified-since 或者If-unmodified-since的協(xié)議頭,包含之前發(fā)送的最后修改時間;或者 If-matchIf-none-match協(xié)議頭,包含之前發(fā)送的ETag。如果頁面的當(dāng)前版本匹配客戶端發(fā)送的ETag,或者如果資源沒有被修改,會發(fā)回304狀態(tài)碼,而不是一個完整的回復(fù),告訴客戶端沒有任何修改。根據(jù)協(xié)議頭,如果頁面被修改了,或者不匹配客戶端發(fā)送的 ETag,會返回412(先決條件失敗,Precondition Failed)狀態(tài)碼。

當(dāng)你需要更多精細(xì)化的控制時,你可以使用每個視圖的按需處理函數(shù)。

Changed in Django 1.8:

向按需視圖處理添加If-unmodified-since協(xié)議頭的支持

The condition

有時(實(shí)際上是經(jīng)常),你可以創(chuàng)建一些函數(shù)來快速計算出資源的ETag值或者最后修改時間,并不需要執(zhí)行構(gòu)建完整視圖所需的所有步驟。Django可以使用這些函數(shù)來為視圖處理提供一個“early bailout”的選項。來告訴客戶端,內(nèi)容自從上次請求并沒有任何改動。

這兩個函數(shù)作為參數(shù)傳遞到django.views.decorators.http.condition裝飾器中。這個裝時期使用這兩個函數(shù)(如果你不能既快又容易得計算出來,你只需要提供一個)來弄清楚是否HTTP請求中的協(xié)議頭匹配那些資源。如果它們不匹配,會生成資源的一份新的副本,并調(diào)用你的普通視圖。

condition裝飾器的簽名為i:

condition(etag_func=None, last_modified_func=None)

計算ETag的最后修改時間的兩個函數(shù),會以相同的順序傳入request對象和相同的參數(shù),就像它們封裝的視圖函數(shù)那樣。last_modified_func函數(shù)應(yīng)該返回一個標(biāo)準(zhǔn)的datetime值,它制訂了資源修改的最后時間,或者資源不存在為 None。傳遞給etag裝飾器的函數(shù)應(yīng)該返回一個表示資源Etag的字符串,或者資源不存在時為None。

用一個例子可以很好展示如何使用這一特性。假設(shè)你有這兩個模型,表示一個簡單的博客系統(tǒng):

import datetime
from django.db import models

class Blog(models.Model):
    ...

class Entry(models.Model):
    blog = models.ForeignKey(Blog)
    published = models.DateTimeField(default=datetime.datetime.now)
    ...

如果頭版展示最后的博客文章,僅僅在你添加新文章的時候修改,你可以非??焖俚赜嬎愠鲎詈笮薷臅r間。你需要這個博客每一篇文章的最后 發(fā)布 日期。實(shí)現(xiàn)它的一種方式是:

def latest_entry(request, blog_id):
    return Entry.objects.filter(blog=blog_id).latest("published").published

接下來你可以使用這個函數(shù),來為你的頭版視圖事先探測未修改的頁面:

from django.views.decorators.http import condition

@condition(last_modified_func=latest_entry)
def front_page(request, blog_id):
    ...

只計算一個值的快捷方式

一個普遍的原則是,如果你提供了計算 ETag_和_最后修改時間的函數(shù),你應(yīng)該這樣做:你并不知道HTTP客戶端會發(fā)給你哪個協(xié)議頭,所以要準(zhǔn)備好處理兩種情況。但是,有時只有二者之一容易計算,并且Django只提供給你計算ETag或最后修改日期的裝飾器。

django.views.decorators.http.etagdjango.views.decorators.http.last_modified作為condition裝飾器,傳入相同類型的函數(shù)。他們的簽名是:

etag(etag_func)
last_modified(last_modified_func)

我們可以編寫一個初期的示例,它僅僅使用最后修改日期的函數(shù),使用這些裝飾器之一:

@last_modified(latest_entry)
def front_page(request, blog_id):
    ...

...或者:

def front_page(request, blog_id):
    ...
front_page = last_modified(latest_entry)(front_page)

Use condition

如果你想要測試兩個先決條件,把etaglast_modified裝飾器鏈到一起看起來很不錯。但是,這會導(dǎo)致不正確的行為:

# Bad code. Don't do this!
@etag(etag_func)
@last_modified(last_modified_func)
def my_view(request):
    # ...

# End of bad code.

第一個裝飾器不知道后面的任何事情,并且可能發(fā)送“未修改”的響應(yīng),即使第二個裝飾器會處理別的事情。condition裝飾器同時更使用兩個回調(diào)函數(shù),來弄清楚哪個是正確的行為。

使用帶有其它HTTP方法的裝飾器

condition裝飾器不僅僅對GETHEAD請求有用(HEAD請求在這種情況下和GET相同)。它也可以用于為 POST, PUTDELETE請求提供檢查。在這些情況下,不是要返回一個“未修改(not modified,314)”的響應(yīng),而是要告訴服務(wù)端,它們嘗試修改的資源在此期間被修改了。

例如,考慮以下客戶端和服務(wù)端之間的交互:

  1. 客戶端請求/foo/。
  2. 服務(wù)端回復(fù)一些帶有"abcd1234"ETag的內(nèi)容。
  3. 客戶端發(fā)送HTTP PUT 請求到 /foo/ 來更新資源。同時也發(fā)送了If-Match: "abcd1234" 協(xié)議頭來指定嘗試更新的版本。
  4. 服務(wù)端檢查是否資源已經(jīng)被修改,通過和GET 上所做的相同方式計算ETag(使用相同的函數(shù))。如果資源 已經(jīng) 修改了,會返回412狀態(tài)碼,意思是“先決條件失?。╬recondition failed)”。
  5. 客戶端在接收到412響應(yīng)之后,發(fā)送 GET請求到 /foo/,來在更新之前獲取內(nèi)容的新版本。

重要的事情是,這個例子展示了在所有情況下,ETag和最后修改時間值都采用相同函數(shù)計算。實(shí)際上,你 應(yīng)該 使用相同函數(shù),以便每次都返回相同的值。

使用中間件按需處理來比較

你可能注意到,Django已經(jīng)通過django.middleware.http.ConditionalGetMiddlewareCommonMiddleware.提供了簡單和直接的GET 的按需處理。這些中間件易于使用并且適用于多種情況,然而它們的功能有一些高級用法上的限制:

  • 它們在全局上用于你項目中的所有視圖。
  • 它們不會代替你生成響應(yīng)本身,這可能要花一些代價。
  • 它們只適用于HTTP GET 請求。

在這里,你應(yīng)該選擇最適用于你特定問題的工具。如果你有辦法快速計算出ETag和修改時間,并且如果一些視圖需要花一些時間來生成內(nèi)容,你應(yīng)該考慮使用這篇文檔描述的condition裝飾器。如果一些都執(zhí)行得非??欤瑘猿质褂弥虚g件在如果視圖沒有修改的條件下也會使發(fā)回客戶端的網(wǎng)絡(luò)流量也會減少。

譯者:Django 文檔協(xié)作翻譯小組,原文:Conditional content processing

本文以 CC BY-NC-SA 3.0 協(xié)議發(fā)布,轉(zhuǎn)載請保留作者署名和文章出處。

Django 文檔協(xié)作翻譯小組人手緊缺,有興趣的朋友可以加入我們,完全公益性質(zhì)。交流群:467338606。

上一篇:開發(fā)過程下一篇:管理操作