動態網站的問題就在于它是動態的。 也就是說每次用戶訪問一個頁面,服務器要執行數據庫查詢,啟動模板,執行業務邏輯以及最終生成一個你所看到的網頁,這一切都是動態即時生成的。 從處理器資源的角度來看,這是比較昂貴的。
對于大多數網絡應用來說,過載并不是大問題。 因為大多數網絡應用并不是washingtonpost.com或Slashdot;它們通常是很小很簡單,或者是中等規模的站點,只有很少的流量。 但是對于中等至大規模流量的站點來說,盡可能地解決過載問題是非常必要的。
這就需要用到緩存了。
緩存的目的是為了避免重復計算,特別是對一些比較耗時間、資源的計算。 下面的偽代碼演示了如何對動態頁面的結果進行緩存。
given a URL, try finding that page in the cache
if the page is in the cache:
return the cached page
else:
generate the page
save the generated page in the cache (for next time)
return the generated page
為此,Django提供了一個穩定的緩存系統讓你緩存動態頁面的結果,這樣在接下來有相同的請求就可以直接使用緩存中的數據,避免不必要的重復計算。 另外Django還提供了不同粒度數據的緩存,例如: 你可以緩存整個頁面,也可以緩存某個部分,甚至緩存整個網站。
Django也和”上游”緩存工作的很好,例如Squid(http://www.squid-cache.org)和基于瀏覽器的緩存。 這些類型的緩存你不直接控制,但是你可以提供關于你的站點哪部分應該被緩存和怎樣緩存的線索(通過HTTP頭部)給它們
緩存系統需要一些少量的設定工作。 也就是說,你必須告訴它緩存的數據應該放在哪里,在數據庫中,在文件系統,或直接在內存中。 這是一個重要的決定,影響您的高速緩存的性能,是的,有些類型的緩存比其它類型快。
緩存設置在settings文件的 CACHE_BACKEND中。 這里是一個CACHE_BACKEND所有可用值的解釋。
Memcached是迄今為止可用于Django的最快,最有效的緩存類型,Memcached是完全基于內存的緩存框架,最初開發它是用以處理高負荷的LiveJournal.com隨后由Danga Interactive公司開源。 它被用于一些站點,例如Facebook和維基百科網站,以減少數據庫訪問,并大幅提高站點的性能。
Memcached是免費的(http://danga.com/memcached)。它作為一個守護進程運行,并分配了特定數量的內存。 它只是提供了添加,檢索和刪除緩存中的任意數據的高速接口。 所有數據都直接存儲在內存中,所以沒有對使用的數據庫或文件系統的開銷。
在安裝了Memcached本身之后,你將需要安裝Memcached Python綁定,它沒有直接和Django綁定。 這兩個可用版本。 選擇和安裝以下模塊之一:
最快的可用選項是一個模塊,稱為cmemcache,在http://gijsbert.org/cmemcache。
若要使用Memcached的Django,設置CACHE_BACKEND到memcached:/ / IP:port/,其中IP是Memcached的守護進程的IP地址,port是Memcached運行的端口。
在這個例子中,Memcached運行在本地主機 (127.0.0.1)上,端口為11211:
CACHE_BACKEND = 'memcached://127.0.0.1:11211/'
Memcached的一個極好的特性是它在多個服務器間分享緩存的能力。 這意味著您可以在多臺機器上運行Memcached的守護進程,該程序會把這些機器當成一個單一緩存,而無需重復每臺機器上的緩存值。 要充分利用此功能,請在CACHE_BACKEND里引入所有服務器的地址,用分號分隔。
這個例子中,緩存在運行在IP地址為172.19.26.240和172.19.26.242,端口號為11211的Memcached實例間分享:
CACHE_BACKEND = 'memcached://172.19.26.240:11211;172.19.26.242:11211/'
這個例子中,緩存在運行在172.19.26.240(端口11211),172.19.26.242(端口11212),172.19.26.244(端口11213)的Memcached實例間分享:
CACHE_BACKEND = 'memcached://172.19.26.240:11211;172.19.26.242:11212;172.19.26.244:11213/'
最后有關Memcached的一點是,基于內存的緩存有一個重大的缺點。 由于緩存的數據存儲在內存中,所以如果您的服務器崩潰,數據將會消失。 顯然,內存不是用來持久化數據的,因此不要把基于內存的緩存作為您唯一的存儲數據緩存。 毫無疑問,在Django的緩存后端不應該用于持久化,它們本來就被設計成緩存的解決方案。但我們仍然指出此點,這里是因為基于內存的緩存是暫時的。
為了使用數據庫表作為緩存后端,首先在數據庫中運行這個命令以創建緩存表:
python manage.py createcachetable [cache_table_name]
這里的[cache_table_name]是要創建的數據庫表名。 (這個名字隨你的便,只要它是一個有效的表名,而且不是已經在您的數據庫中使用的表名。)這個命令以Django的數據庫緩存系統所期望的格式創建一個表。
一旦你創建了數據庫表,把你的CACHE_BACKEND設置為”db://tablename”,這里的tablename是數據庫表的名字,在這個例子中,緩存表名為my_cache_table: 在這個例子中,高速緩存表的名字是my_cache_table:
CACHE_BACKEND = 'db://my_cache_table'
數據庫緩存后端使用你的settings文件指定的同一數據庫。 你不能為你的緩存表使用不同的數據庫后端.
如果你已經有了一個快速,良好的索引數據庫服務器,那么數據庫緩存的效果最明顯。
要把緩存項目放在文件系統上,請為CACHE_BACKEND使用”file://“的緩存類型。例如,要把緩存數據存儲在/var/tmp/django_cache上,請使用此設置:
CACHE_BACKEND = 'file:///var/tmp/django_cache'
注意例子中開頭有三個斜線。 頭兩項是file://,第三個是第一個字符的目錄路徑,/var/tmp/django_cache。如果你使用的是Windows,在file://之后加上文件的驅動器號:
file://c:/foo/bar
目錄路徑應該是絕對路徑,即應該以你的文件系統的根開始。 在設置的結尾放置斜線與否無關緊要。
確認該設置指向的目錄存在并且你的Web服務器運行的系統的用戶可以讀寫該目錄。 繼續上面的例子,如果你的服務器以用戶apache運行,確認/var/tmp/django_cache存在并且用戶apache可以讀寫/var/tmp/django_cache目錄。
每個緩存值將被存儲為單獨的文件,其內容是Python的pickle模塊以序列化(“pickled”)形式保存的緩存數據。 每個文件的名稱是緩存鍵,以規避開安全文件系統的使用。
如果你想利用內存緩存的速度優勢,但又不能使用Memcached,可以考慮使用本地存儲器緩存后端。 此緩存的多進程和線程安全。 設置 CACHE_BACKEND 為 locmem:/// 來使用它,例如:
CACHE_BACKEND = 'locmem:///'
請注意,每個進程都有自己私有的緩存實例,這意味著跨進程緩存是不可能的。 這顯然也意味著本地內存緩存效率并不是特別高,所以對產品環境來說它可能不是一個好選擇。 對開發來說還不錯。
最后,Django提供了一個假緩存(只是實現了緩存接口,實際上什么都不做)。
假如你有一個產品站點,在許多地方使用高度緩存,但在開發/測試環境中,你不想緩存,也不想改變代碼,這就非常有用了。 要激活虛擬緩存,就像這樣設置CACHE_BACKEND:
CACHE_BACKEND = 'dummy:///'
盡管Django包含對許多緩存后端的支持,在某些情況下,你仍然想使用自定義緩存后端。 要讓Django使用外部緩存后端,需要使用一個Python import路徑作為的CACHE_BACKEND URI的(第一個冒號前的部分),像這樣:
CACHE_BACKEND = 'path.to.backend://'
如果您構建自己的后端,你可以參考標準緩存后端的實現。 源代碼在Django的代碼目錄的django/core/cache/backends/下。
注意 如果沒有一個真正令人信服的理由,比如主機不支持,你就應該堅持使用Django包含的緩存后端。 它們經過大量測試,并且易于使用。
每個緩存后端都可能使用參數。 它們在CACHE_BACKEND設置中以查詢字符串形式給出。 有效參數如下:
timeout:用于緩存的過期時間,以秒為單位。 這個參數默認被設置為300秒(五分鐘)。
max_entries:對于內存,文件系統和數據庫后端,高速緩存允許的最大條目數,超出這個數則舊值將被刪除。 這個參數默認是300。
cull_percentage :當達到 max_entries 的時候,被刪除的條目比率。 實際的比率是 1/cull_percentage ,所以設置cull_frequency=2就是在達到 max_entries 的時候去除一半數量的緩存。
把 cull_frequency 的值設置為 0 意味著當達到 max_entries 時,緩存將被清空。 這將以很多緩存丟失為代價,大大提高接受訪問的速度。
在這個例子中, timeout 被設成 60
CACHE_BACKEND = "memcached://127.0.0.1:11211/?timeout=60"
而在這個例子中, timeout 設為 30 而 max_entries 為 400 :
CACHE_BACKEND = "locmem:///?timeout=30&max_entries=400"
其中,非法的參數與非法的參數值都將被忽略。
一旦高速緩存設置,最簡單的方法是使用緩存緩存整個網站。 您 需要添加’django.middleware.cache.UpdateCacheMiddleware’和 ‘django.middleware.cache.FetchFromCacheMiddleware’到您的MIDDLEWARE_CLASSES設置中,在這個例子中是:
MIDDLEWARE_CLASSES = (
'django.middleware.cache.UpdateCacheMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.cache.FetchFromCacheMiddleware',
)
注意:
不,這里并沒有排版錯誤: 修改的中間件,必須放在列表的開始位置,而fectch中間件,必須放在最后。 細節有點費解,如果您想了解完整內幕請參看下面的MIDDLEWARE_CLASSES順序。
然后,在你的Django settings文件里加入下面所需的設置:
CACHE_MIDDLEWARE_SECONDS :每個頁面應該被緩存的秒數。
緩存中間件緩存每個沒有GET或者POST參數的頁面。 或者,如果CACHE_MIDDLEWARE_ANONYMOUS_ONLY設置為True,只有匿名請求(即不是由登錄的用戶)將被緩存。 如果想取消用戶相關頁面(user-specific pages)的緩存,例如Djangos 的管理界面,這是一種既簡單又有效的方法。 CACHE_MIDDLEWARE_ANONYMOUS_ONLY,你應該確保你已經啟動AuthenticationMiddleware。
此外,緩存中間件為每個HttpResponse自動設置了幾個頭部信息:
當一個新(沒緩存的)版本的頁面被請求時設置Last-Modified頭部為當前日期/時間。
設置Expires頭部為當前日期/時間加上定義的CACHE_MIDDLEWARE_SECONDS。
參閱更多的中間件第17章。
如果視圖設置自己的緩存到期時間(即 它有一個最大年齡在頭部信息的Cache-Control中),那么頁面將緩存直到過期,而不是CACHE_MIDDLEWARE_SECONDS。使用django.views.decorators.cache裝飾器,您可以輕松地設置視圖的到期時間(使用cache_control裝飾器)或禁用緩存視圖(使用never_cache裝飾器)。 請參閱下面的”使用其他頭部信息“小節以了解裝飾器的更多信息。
更加顆粒級的緩存框架使用方法是對單個視圖的輸出進行緩存。 django.views.decorators.cache定義了一個自動緩存視圖響應的cache_page裝飾器。 他是很容易使用的:
from django.views.decorators.cache import cache_page
def my_view(request):
# ...
my_view = cache_page(my_view, 60 * 15)
也可以使用Python2.4的裝飾器語法:
@cache_page(60 * 15)
def my_view(request):
# ...
cache_page 只接受一個參數: 以秒計的緩存超時時間。 在前例中, “my_view()” 視圖的結果將被緩存 15 分鐘。 (注意: 為了提高可讀性,該參數被書寫為 60 15 。 60 15 將被計算為 900 ,也就是說15 分鐘乘以每分鐘 60 秒。)
和站點緩存一樣,視圖緩存與 URL 無關。 如果多個 URL 指向同一視圖,每個視圖將會分別緩存。 繼續my_view 范例,如果 URLconf 如下所示:
urlpatterns = ('',
(r'^foo/(\d{1,2})/$', my_view),
)
那么正如你所期待的那樣,發送到 /foo/1/ 和 /foo/23/ 的請求將會分別緩存。 但一旦發出了特定的請求(如: /foo/23/ ),之后再度發出的指向該 URL 的請求將使用緩存。
前一節中的范例將視圖硬編碼為使用緩存,因為 cache_page 在適當的位置對 my_view 函數進行了轉換。 該方法將視圖與緩存系統進行了耦合,從幾個方面來說并不理想。 例如,你可能想在某個無緩存的站點中重用該視圖函數,或者你可能想將該視圖發布給那些不想通過緩存使用它們的人。 解決這些問題的方法是在 URLconf 中指定視圖緩存,而不是緊挨著這些視圖函數本身來指定。
完成這項工作非常簡單: 在 URLconf 中用到這些視圖函數的時候簡單地包裹一個 cache_page 。以下是剛才用到過的 URLconf : 這是之前的URLconf:
urlpatterns = ('',
(r'^foo/(\d{1,2})/$', my_view),
)
以下是同一個 URLconf ,不過用 cache_page 包裹了 my_view :
from django.views.decorators.cache import cache_page
urlpatterns = ('',
(r'^foo/(\d{1,2})/$', cache_page(my_view, 60 * 15)),
)
如果采取這種方法, 不要忘記在 URLconf 中導入 cache_page。
你同樣可以使用cache標簽來緩存模板片段。 在模板的頂端附近加入{% load cache %}以通知模板存取緩存標簽。
模板標簽{% cache %}在給定的時間內緩存了塊的內容。 它至少需要兩個參數: 緩存超時時間(以秒計)和指定緩存片段的名稱。 示例:
{% load cache %}
{% cache 500 sidebar %}
.. sidebar ..
{% endcache %}
有時你可能想緩存基于片段的動態內容的多份拷貝。 比如,你想為上一個例子的每個用戶分別緩存側邊欄。 這樣只需要給{% cache %}傳遞額外的參數以標識緩存片段。
{% load cache %}
{% cache 500 sidebar request.user.username %}
.. sidebar for logged in user ..
{% endcache %}
傳遞不止一個參數也是可行的。 簡單地把參數傳給{% cache %}。
緩存超時時間可以作為模板變量,只要它可以解析為整數值。 例如,如果模板變量my_timeout值為600,那么以下兩個例子是等價的。
{% cache 600 sidebar %} ... {% endcache %}
{% cache my_timeout sidebar %} ... {% endcache %}
這個特性在避免模板重復方面非常有用。 可以把超時時間保存在變量里,然后在別的地方復用。
有些時候,對整個經解析的頁面進行緩存并不會給你帶來太多好處,事實上可能會過猶不及。
比如說,也許你的站點所包含的一個視圖依賴幾個費時的查詢,每隔一段時間結果就會發生變化。 在這種情況下,使用站點級緩存或者視圖級緩存策略所提供的整頁緩存并不是最理想的,因為你可能不會想對整個結果進行緩存(因為一些數據經常變化),但你仍然會想對很少變化的部分進行緩存。
針對這樣的情況,Django提供了簡單低級的緩存API。 你可以通過這個API,以任何你需要的粒度來緩存對象。 你可以對所有能夠安全進行 pickle 處理的 Python 對象進行緩存: 字符串、字典和模型對象列表等等。 (查閱 Python 文檔可以了解到更多關于 pickling 的信息。)
緩存模塊django.core.cache擁有一個自動依據CACHE_BACKEND設置創建的django.core.cache對象。
>>> from django.core.cache import cache
基本的接口是 set(key, value, timeout_seconds) 和 get(key) :
>>> cache.set('my_key', 'hello, world!', 30)
>>> cache.get('my_key')
'hello, world!'
timeout_seconds 參數是可選的, 并且默認為前面講過的 CACHE_BACKEND 設置中的 timeout 參數.
如果緩存中不存在該對象,那么cache.get()會返回None。
# Wait 30 seconds for 'my_key' to expire...
>>> cache.get('my_key')
None
我們不建議在緩存中保存 None 常量,因為你將無法區分你保存的 None 變量及由返回值 None 所標識的緩存未命中。
cache.get() 接受一個 缺省 參數。 它指定了當緩存中不存在該對象時所返回的值:
>>> cache.get('my_key', 'has expired')
'has expired'
使用add()方法來新增一個原來沒有的鍵值。 它接受的參數和set()一樣,但是并不去嘗試更新已經存在的鍵值。
>>> cache.set('add_key', 'Initial value')
>>> cache.add('add_key', 'New value')
>>> cache.get('add_key')
'Initial value'
如果想確定add()是否成功添加了緩存值,你應該測試返回值。 成功返回True,失敗返回False。
還有個get_many()接口。 get_many() 所返回的字典包括了你所請求的存在于緩存中且未超時的所有鍵值。
>>> cache.set('a', 1)
>>> cache.set('b', 2)
>>> cache.set('c', 3)
>>> cache.get_many(['a', 'b', 'c'])
{'a': 1, 'b': 2, 'c': 3}
最后,你可以用 cache.delete() 顯式地刪除關鍵字。
>>> cache.delete('a')
也可以使用incr()或者decr()來增加或者減少已經存在的鍵值。 默認情況下,增加或減少的值是1。可以用參數來制定其他值。 如果嘗試增減不存在的鍵值會拋出ValueError。
>>> cache.set('num', 1)
>>> cache.incr('num')
2
>>> cache.incr('num', 10)
12
>>> cache.decr('num')
11
>>> cache.decr('num', 5)
6
注意
incr()/decr()方法不是原子操作。 在支持原子增減的緩存后端上(最著名的是memcached),增減操作才是原子的。 然而,如果后端并不原生支持增減操作,也可以通過取值/更新兩步操作來實現。
目前為止,本章的焦點一直是對你 自己的 數據進行緩存。 但還有一種與 Web 開發相關的緩存: 上游緩存。 有一些系統甚至在請求到達站點之前就為用戶進行頁面緩存。
下面是上游緩存的幾個例子:
你的 ISP (互聯網服務商)可能會對特定的頁面進行緩存,因此如果你向 http://example.com/ 請求一個頁面,你的 ISP 可能無需直接訪問 example.com 就能將頁面發送給你。 而 example.com 的維護者們卻無從得知這種緩存,ISP 位于 example.com 和你的網頁瀏覽器之間,透明地處理所有的緩存。
你的 Django 網站可能位于某個 代理緩存 之后,例如 Squid 網頁代理緩存 (http://www.squid-cache.org/),該緩存為提高性能而對頁面進行緩存。 在此情況下 ,每個請求將首先由代理服務器進行處理,然后僅在需要的情況下才被傳遞至你的應用程序。
上游緩存將會產生非常明顯的效率提升,但也存在一定風險。 許多網頁的內容依據身份驗證以及許多其他變量的情況發生變化,緩存系統僅盲目地根據 URL 保存頁面,可能會向這些頁面的后續訪問者暴露不正確或者敏感的數據。
舉個例子,假定你在使用網頁電郵系統,顯然收件箱頁面的內容取決于登錄的是哪個用戶。 如果 ISP 盲目地緩存了該站點,那么第一個用戶通過該 ISP 登錄之后,他(或她)的用戶收件箱頁面將會緩存給后續的訪問者。 這一點也不好玩。
幸運的是, HTTP 提供了解決該問題的方案。 已有一些 HTTP 頭標用于指引上游緩存根據指定變量來區分緩存內容,并通知緩存機制不對特定頁面進行緩存。 我們將在本節后續部分將對這些頭標進行闡述。
Vary 頭部定義了緩存機制在構建其緩存鍵值時應當將哪個請求頭標考慮在內。 例如,如果網頁的內容取決于用戶的語言偏好,該頁面被稱為根據語言而不同。
缺省情況下,Django 的緩存系統使用所請求的路徑(比如:"/stories/2005/jun/23/bank_robbed/" )來創建其緩存鍵。這意味著每次請求都會使用同樣的緩存版本,不考慮才客戶端cookie和語言配置的不同。 除非你使用Vary頭部通知緩存機制頁面輸出要依據請求頭里的cookie,語言等的設置而不同。
要在 Django 完成這項工作,可使用便利的 vary_on_headers 視圖裝飾器,如下所示:
from django.views.decorators.vary import vary_on_headers
# Python 2.3 syntax.
def my_view(request):
# ...
my_view = vary_on_headers(my_view, 'User-Agent')
# Python 2.4+ decorator syntax.
@vary_on_headers('User-Agent')
def my_view(request):
# ...
在這種情況下,緩存機制(如 Django 自己的緩存中間件)將會為每一個單獨的用戶瀏覽器緩存一個獨立的頁面版本。
使用 vary_on_headers 裝飾器而不是手動設置 Vary 頭部(使用像 response['Vary'] = 'user-agent' 之類的代碼)的好處是修飾器在(可能已經存在的) Vary 之上進行 添加 ,而不是從零開始設置,且可能覆蓋該處已經存在的設置。
你可以向 vary_on_headers() 傳入多個頭標:
@vary_on_headers('User-Agent', 'Cookie')
def my_view(request):
# ...
該段代碼通知上游緩存對 兩者 都進行不同操作,也就是說 user-agent 和 cookie 的每種組合都應獲取自己的緩存值。 舉例來說,使用 Mozilla 作為 user-agent 而 foo=bar 作為 cookie 值的請求應該和使用 Mozilla 作為 user-agent 而 foo=ham 的請求應該被視為不同請求。
由于根據 cookie 而區分對待是很常見的情況,因此有 vary_on_cookie 裝飾器。 以下兩個視圖是等效的:
@vary_on_cookie
def my_view(request):
# ...
@vary_on_headers('Cookie')
def my_view(request):
# ...
傳入 vary_on_headers 頭標是大小寫不敏感的; "User-Agent" 與 "user-agent" 完全相同。
你也可以直接使用幫助函數:django.utils.cache.patch_vary_headers。 該函數設置或增加 Vary header ,例如:
from django.utils.cache import patch_vary_headers
def my_view(request):
# ...
response = render_to_response('template_name', context)
patch_vary_headers(response, ['Cookie'])
return response
patch_vary_headers 以一個 HttpResponse 實例為第一個參數,以一個大小寫不敏感的頭標名稱列表或元組為第二個參數。
關于緩存剩下的問題是數據的隱私性以及在級聯緩存中數據應該在何處儲存的問題。
通常用戶將會面對兩種緩存: 他或她自己的瀏覽器緩存(私有緩存)以及他或她的提供者緩存(公共緩存)。 公共緩存由多個用戶使用,而受其他某人的控制。 這就產生了你不想遇到的敏感數據的問題,比如說你的銀行賬號被存儲在公眾緩存中。 因此,Web 應用程序需要以某種方式告訴緩存那些數據是私有的,哪些是公共的。
解決方案是標示出某個頁面緩存應當是私有的。 要在 Django 中完成此項工作,可使用 cache_control 視圖修飾器: 例如:
from django.views.decorators.cache import cache_control
@cache_control(private=True)
def my_view(request):
# ...
該修飾器負責在后臺發送相應的 HTTP 頭部。
還有一些其他方法可以控制緩存參數。 例如, HTTP 允許應用程序執行如下操作:
定義頁面可以被緩存的最大時間。
在 Django 中,可使用 cache_control 視圖修飾器指定這些緩存參數。 在本例中, cache_control 告訴緩存對每次訪問都重新驗證緩存并在最長 3600 秒內保存所緩存版本:
from django.views.decorators.cache import cache_control
@cache_control(must_revalidate=True, max_age=3600)
def my_view(request):
# ...
在 cache_control() 中,任何合法的Cache-Control HTTP 指令都是有效的。下面是完整列表:
public=True
private=True
no_cache=True
no_transform=True
must_revalidate=True
proxy_revalidate=True
max_age=num_seconds
緩存中間件已經使用 CACHE_MIDDLEWARE_SETTINGS 設置設定了緩存頭部 max-age 。 如果你在cache_control修飾器中使用了自定義的max_age,該修飾器將會取得優先權,該頭部的值將被正確地被合并。
如果你想用頭部完全禁掉緩存,django.views.decorators.cache.never_cache裝飾器可以添加確保響應不被緩存的頭部信息。 例如:
from django.views.decorators.cache import never_cache
@never_cache
def myview(request):
# ...
Django 帶有一些其它中間件可幫助您優化應用程序的性能:
django.middleware.http.ConditionalGetMiddleware 為現代瀏覽器增加了有條件的,基于 ETag 和Last-Modified 頭標的GET響應的相關支持。
如果使用緩存中間件,注意在MIDDLEWARE_CLASSES設置中正確配置。 因為緩存中間件需要知道哪些頭部信息由哪些緩存區來區分。 中間件總是盡可能得想Vary響應頭中添加信息。
UpdateCacheMiddleware在相應階段運行。因為中間件是以相反順序運行的,所有列表頂部的中間件反而_last_在相應階段的最后運行。 所有,你需要確保UpdateCacheMiddleware排在任何可能往_Vary_頭部添加信息的中間件之前。 下面的中間件模塊就是這樣的:
添加 Cookie 的 SessionMiddleware
添加 Accept-Encoding 的 GZipMiddleware
另一方面,FetchFromCacheMiddleware在請求階段運行,這時中間件循序執行,所以列表頂端的項目會_首先_執行。 FetchFromCacheMiddleware也需要在會修改Vary頭部的中間件之后運行,所以FetchFromCacheMiddleware必須放在它們后面。
Django捆綁了一系列可選的方便特性。 我們已經介紹了一些: admin站點(第六章)和session/user框架(第十四章)。 下一章中,我們將講述Django中其他的子框架。