django 1.8 官方文档翻译:14-1 按需内容处理

简介: 按需内容处理HTTP客户端可能发送一些协议头来告诉服务端它们已经看过了哪些资源。这在获取网页(使用HTTPGET请求)时非常常见,可以避免发送客户端已经获得的完整数据。

按需内容处理

HTTP客户端可能发送一些协议头来告诉服务端它们已经看过了哪些资源。这在获取网页(使用HTTPGET请求)时非常常见,可以避免发送客户端已经获得的完整数据。然而,相同的协议头可用于所有HTTP方法(POST, PUT, DELETE, 以及其它)。

对于每一个Django从视图发回的页面(响应),都会提供两个HTTP协议头:ETagLast-Modified。这些协议头在HTTP响应中是可选的。它们可以由你的视图函数设置,或者你可以依靠 CommonMiddleware 中间件来设置ETag 协议头。

当你的客户端再次请求相同的资源时,它可能会发送 If-modified-since 或者If-unmodified-since的协议头,包含之前发送的最后修改时间;或者 If-matchIf-none-match协议头,包含之前发送的ETag。如果页面的当前版本匹配客户端发送的ETag,或者如果资源没有被修改,会发回304状态码,而不是一个完整的回复,告诉客户端没有任何修改。根据协议头,如果页面被修改了,或者不匹配客户端发送的 ETag,会返回412(先决条件失败,Precondition Failed)状态码。

当你需要更多精细化的控制时,你可以使用每个视图的按需处理函数。

Changed in Django 1.8:

向按需视图处理添加If-unmodified-since协议头的支持

The condition

有时(实际上是经常),你可以创建一些函数来快速计算出资源的ETag值或者最后修改时间,并不需要执行构建完整视图所需的所有步骤。Django可以使用这些函数来为视图处理提供一个“early bailout”的选项。来告诉客户端,内容自从上次请求并没有任何改动。

这两个函数作为参数传递到django.views.decorators.http.condition装饰器中。这个装时期使用这两个函数(如果你不能既快又容易得计算出来,你只需要提供一个)来弄清楚是否HTTP请求中的协议头匹配那些资源。如果它们不匹配,会生成资源的一份新的副本,并调用你的普通视图。

condition装饰器的签名为i:

condition(etag_func=None, last_modified_func=None)

计算ETag的最后修改时间的两个函数,会以相同的顺序传入request对象和相同的参数,就像它们封装的视图函数那样。last_modified_func函数应该返回一个标准的datetime值,它制订了资源修改的最后时间,或者资源不存在为 None。传递给etag装饰器的函数应该返回一个表示资源Etag的字符串,或者资源不存在时为None

用一个例子可以很好展示如何使用这一特性。假设你有这两个模型,表示一个简单的博客系统:

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)
    ...

如果头版展示最后的博客文章,仅仅在你添加新文章的时候修改,你可以非常快速地计算出最后修改时间。你需要这个博客每一篇文章的最后 发布 日期。实现它的一种方式是:

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

接下来你可以使用这个函数,来为你的头版视图事先探测未修改的页面:

from django.views.decorators.http import condition

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

只计算一个值的快捷方式

一个普遍的原则是,如果你提供了计算 ETag_和_最后修改时间的函数,你应该这样做:你并不知道HTTP客户端会发给你哪个协议头,所以要准备好处理两种情况。但是,有时只有二者之一容易计算,并且Django只提供给你计算ETag或最后修改日期的装饰器。

django.views.decorators.http.etagdjango.views.decorators.http.last_modified作为condition装饰器,传入相同类型的函数。他们的签名是:

etag(etag_func)
last_modified(last_modified_func)

我们可以编写一个初期的示例,它仅仅使用最后修改日期的函数,使用这些装饰器之一:

@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装饰器链到一起看起来很不错。但是,这会导致不正确的行为:

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

# End of bad code.

第一个装饰器不知道后面的任何事情,并且可能发送“未修改”的响应,即使第二个装饰器会处理别的事情。condition装饰器同时更使用两个回调函数,来弄清楚哪个是正确的行为。

使用带有其它HTTP方法的装饰器

condition装饰器不仅仅对GETHEAD请求有用(HEAD请求在这种情况下和GET相同)。它也可以用于为 POST, PUTDELETE请求提供检查。在这些情况下,不是要返回一个“未修改(not modified,314)”的响应,而是要告诉服务端,它们尝试修改的资源在此期间被修改了。

例如,考虑以下客户端和服务端之间的交互:

  1. 客户端请求/foo/
  2. 服务端回复一些带有"abcd1234"ETag的内容。
  3. 客户端发送HTTP PUT 请求到 /foo/ 来更新资源。同时也发送了If-Match: "abcd1234" 协议头来指定尝试更新的版本。
  4. 服务端检查是否资源已经被修改,通过和GET 上所做的相同方式计算ETag(使用相同的函数)。如果资源 已经 修改了,会返回412状态码,意思是“先决条件失败(precondition failed)”。
  5. 客户端在接收到412响应之后,发送 GET请求到 /foo/,来在更新之前获取内容的新版本。

重要的事情是,这个例子展示了在所有情况下,ETag和最后修改时间值都采用相同函数计算。实际上,你 应该 使用相同函数,以便每次都返回相同的值。

使用中间件按需处理来比较

你可能注意到,Django已经通过django.middleware.http.ConditionalGetMiddlewareCommonMiddleware.提供了简单和直接的GET 的按需处理。这些中间件易于使用并且适用于多种情况,然而它们的功能有一些高级用法上的限制:

  • 它们在全局上用于你项目中的所有视图。
  • 它们不会代替你生成响应本身,这可能要花一些代价。
  • 它们只适用于HTTP GET 请求。

在这里,你应该选择最适用于你特定问题的工具。如果你有办法快速计算出ETag和修改时间,并且如果一些视图需要花一些时间来生成内容,你应该考虑使用这篇文档描述的condition装饰器。如果一些都执行得非常快,坚持使用中间件在如果视图没有修改的条件下也会使发回客户端的网络流量也会减少。

译者:Django 文档协作翻译小组,原文:Conditional content processing

本文以 CC BY-NC-SA 3.0 协议发布,转载请保留作者署名和文章出处。

Django 文档协作翻译小组人手紧缺,有兴趣的朋友可以加入我们,完全公益性质。交流群:467338606。

相关文章
|
算法 调度 Python
django 1.8 官方文档翻译: 3-1-1 URL调度器
URL调度器 简洁、优雅的URL 模式在高质量的Web 应用中是一个非常重要的细节。Django 允许你任意设计你的URL,不受框架束缚。
880 0
|
API Python 调度
django 1.8 官方文档翻译: 3-4-1 基于类的视图
基于类的视图 视图是一个可调用对象,它接收一个请求然后返回一个响应。这个可调用对象可以不只是函数,Django 提供一些可以用作视图的类。
752 0
|
前端开发 Python
django 1.8 官方文档翻译: 3-4-3 使用基于类的视图处理表单
使用基于类的视图处理表单 表单的处理通常有3 个步骤: 初始的的GET (空白或预填充的表单) 带有非法数据的POST(通常重新显示表单和错误信息) 带有合法数据的POST(处理数据并重定向) 你自己实现这些功能经常导致许多重复的样本代码(参见在视图中使用表单)。
920 0
|
Python 网络架构 前端开发
django 1.8 官方文档翻译: 3-2-1 内建的视图
内建的视图 有几个Django 的内建视图在编写视图 中讲述,文档的其它地方也会有所讲述。 开发环境中的文件服务器 static.serve(request, path, document_root, show_indexes=False) 在本地的开发环境中,除了你的项目中的静态文件,可能还有一些文件,出于方便,你希望让Django 来作为服务器。
1050 0
|
缓存 Python 安全
django 1.8 官方文档翻译: 3-1-4 视图装饰器
视图装饰器 Django为视图提供了数个装饰器,用以支持相关的HTTP服务。 允许的HTTP 方法 django.views.decorators.http 包里的装饰器可以基于请求的方法来限制对视图的访问。
964 0
|
Web App开发 缓存 安全
django 1.8 官方文档翻译: 3-6-2 内建的中间件
Django 文档协作翻译小组人手紧缺,有兴趣的朋友可以加入我们,完全公益性质。 交流群:467338606 网站:http://python.usyiyi.cn/django/index.html 中间件 这篇文档介绍了Django自带的所有中间件组件。
1213 0
|
Python
django 1.8 官方文档翻译: 4-2-4 人性化
Django 文档协作翻译小组人手紧缺,有兴趣的朋友可以加入我们,完全公益性质。 交流群:467338606 网站:http://python.usyiyi.cn/django/index.html django.contrib.humanize 一系列Django的模板过滤器,有助于向数据添加“人文关怀”。
777 0
|
数据库 Python 开发者
django 1.8 官方文档翻译: 3-4-2 内建显示视图
Django 文档协作翻译小组人手紧缺,有兴趣的朋友可以加入我们,完全公益性质。 交流群:467338606 网站:http://python.usyiyi.cn/django/index.html 基于类的内建通用视图 编写Web应用可能是单调的,因为你需要不断的重复某一种模式。
737 0
|
中间件 Python
django 1.8 官方文档翻译: 3-6-1 中间件概览
Django 文档协作翻译小组人手紧缺,有兴趣的朋友可以加入我们,完全公益性质。 交流群:467338606 网站:http://python.usyiyi.cn/django/index.html 中间件 中间件是一个介入Django的请求和响应的处理过程中的钩子框架。
1027 2
|
SQL 测试技术 数据库
django 1.8 官方文档翻译: 2-6-3 提供初始数据
Django 文档协作翻译小组人手紧缺,有兴趣的朋友可以加入我们,完全公益性质。 交流群:467338606 网站:http://python.usyiyi.cn/django/index.html 为模型提供初始数据 当你首次建立一个应用的时候,为你的数据库预先安装一些硬编码的数据,是很有用处的。
995 0