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