1 ===========================
2 Conditional View Processing
3 ===========================
5 HTTP clients can send a number of headers to tell the server about copies of a
6 resource that they have already seen. This is commonly used when retrieving a
7 Web page (using an HTTP ``GET`` request) to avoid sending all the data for
8 something the client has already retrieved. However, the same headers can be
9 used for all HTTP methods (``POST``, ``PUT``, ``DELETE``, etc).
11 For each page (response) that Django sends back from a view, it might provide
12 two HTTP headers: the ``ETag`` header and the ``Last-Modified`` header. These
13 headers are optional on HTTP responses. They can be set by your view function,
14 or you can rely on the :class:`~django.middleware.common.CommonMiddleware`
15 middleware to set the ``ETag`` header.
17 When the client next requests the same resource, it might send along a header
18 such as `If-modified-since`_, containing the date of the last modification
19 time it was sent, or `If-none-match`_, containing the ``ETag`` it was sent.
20 If the current version of the page matches the ``ETag`` sent by the client, or
21 if the resource has not been modified, a 304 status code can be sent back,
22 instead of a full response, telling the client that nothing has changed.
24 .. _If-none-match: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.26
25 .. _If-modified-since: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.25
27 When you need more fine-grained control you may use per-view conditional
30 .. conditional-decorators:
32 The ``condition`` decorator
33 ===========================
35 Sometimes (in fact, quite often) you can create functions to rapidly compute the ETag_
36 value or the last-modified time for a resource, **without** needing to do all
37 the computations needed to construct the full view. Django can then use these
38 functions to provide an "early bailout" option for the view processing.
39 Telling the client that the content has not been modified since the last
42 .. _ETag: http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.11
44 These two functions are passed as parameters the
45 ``django.views.decorators.http.condition`` decorator. This decorator uses
46 the two functions (you only need to supply one, if you can't compute both
47 quantities easily and quickly) to work out if the headers in the HTTP request
48 match those on the resource. If they don't match, a new copy of the resource
49 must be computed and your normal view is called.
51 The ``condition`` decorator's signature looks like this::
53 condition(etag_func=None, last_modified_func=None)
55 The two functions, to compute the ETag and the last modified time, will be
56 passed the incoming ``request`` object and the same parameters, in the same
57 order, as the view function they are helping to wrap. The function passed
58 ``last_modified_func`` should return a standard datetime value specifying the
59 last time the resource was modified, or ``None`` if the resource doesn't
60 exist. The function passed to the ``etag`` decorator should return a string
61 representing the `Etag`_ for the resource, or ``None`` if it doesn't exist.
63 Using this feature usefully is probably best explained with an example.
64 Suppose you have this pair of models, representing a simple blog system::
67 from django.db import models
69 class Blog(models.Model):
72 class Entry(models.Model):
73 blog = models.ForeignKey(Blog)
74 published = models.DateTimeField(default=datetime.datetime.now)
77 If the front page, displaying the latest blog entries, only changes when you
78 add a new blog entry, you can compute the last modified time very quickly. You
79 need the latest ``published`` date for every entry associated with that blog.
80 One way to do this would be::
82 def latest_entry(request, blog_id):
83 return Entry.objects.filter(blog=blog_id).latest("published").published
85 You can then use this function to provide early detection of an unchanged page
86 for your front page view::
88 from django.views.decorators.http import condition
90 @condition(last_modified_func=latest_entry)
91 def front_page(request, blog_id):
94 Shortcuts for only computing one value
95 ======================================
97 As a general rule, if you can provide functions to compute *both* the ETag and
98 the last modified time, you should do so. You don't know which headers any
99 given HTTP client will send you, so be prepared to handle both. However,
100 sometimes only one value is easy to compute and Django provides decorators
101 that handle only ETag or only last-modified computations.
103 The ``django.views.decorators.http.etag`` and
104 ``django.views.decorators.http.last_modified`` decorators are passed the same
105 type of functions as the ``condition`` decorator. Their signatures are::
108 last_modified(last_modified_func)
110 We could write the earlier example, which only uses a last-modified function,
111 using one of these decorators::
113 @last_modified(latest_entry)
114 def front_page(request, blog_id):
119 def front_page(request, blog_id):
121 front_page = last_modified(latest_entry)(front_page)
123 Use ``condition`` when testing both conditions
124 ------------------------------------------------
126 It might look nicer to some people to try and chain the ``etag`` and
127 ``last_modified`` decorators if you want to test both preconditions. However,
128 this would lead to incorrect behavior.
132 # Bad code. Don't do this!
134 @last_modified(last_modified_func)
135 def my_view(request):
140 The first decorator doesn't know anything about the second and might
141 answer that the response is not modified even if the second decorators would
142 determine otherwise. The ``condition`` decorator uses both callback functions
143 simultaneously to work out the right action to take.
145 Using the decorators with other HTTP methods
146 ============================================
148 The ``condition`` decorator is useful for more than only ``GET`` and
149 ``HEAD`` requests (``HEAD`` requests are the same as ``GET`` in this
150 situation). It can be used also to be used to provide checking for ``POST``,
151 ``PUT`` and ``DELETE`` requests. In these situations, the idea isn't to return
152 a "not modified" response, but to tell the client that the resource they are
153 trying to change has been altered in the meantime.
155 For example, consider the following exchange between the client and server:
157 1. Client requests ``/foo/``.
158 2. Server responds with some content with an ETag of ``"abcd1234"``.
159 3. Client sends an HTTP ``PUT`` request to ``/foo/`` to update the
160 resource. It also sends an ``If-Match: "abcd1234"`` header to specify
161 the version it is trying to update.
162 4. Server checks to see if the resource has changed, by computing the ETag
163 the same way it does for a ``GET`` request (using the same function).
164 If the resource *has* changed, it will return a 412 status code code,
165 meaning "precondition failed".
166 5. Client sends a ``GET`` request to ``/foo/``, after receiving a 412
167 response, to retrieve an updated version of the content before updating
170 The important thing this example shows is that the same functions can be used
171 to compute the ETag and last modification values in all situations. In fact,
172 you **should** use the same functions, so that the same values are returned
175 Comparison with middleware conditional processing
176 =================================================
178 You may notice that Django already provides simple and straightforward
179 conditional ``GET`` handling via the
180 :class:`django.middleware.http.ConditionalGetMiddleware` and
181 :class:`~django.middleware.common.CommonMiddleware`. Whilst certainly being
182 easy to use and suitable for many situations, those pieces of middleware
183 functionality have limitations for advanced usage:
185 * They are applied globally to all views in your project
186 * They don't save you from generating the response itself, which may be
188 * They are only appropriate for HTTP ``GET`` requests.
190 You should choose the most appropriate tool for your particular problem here.
191 If you have a way to compute ETags and modification times quickly and if some
192 view takes a while to generate the content, you should consider using the
193 ``condition`` decorator described in this document. If everything already runs
194 fairly quickly, stick to using the middleware and the amount of network
195 traffic sent back to the clients will still be reduced if the view hasn't