diff options
Diffstat (limited to 'parts/django/docs/topics/conditional-view-processing.txt')
-rw-r--r-- | parts/django/docs/topics/conditional-view-processing.txt | 199 |
1 files changed, 0 insertions, 199 deletions
diff --git a/parts/django/docs/topics/conditional-view-processing.txt b/parts/django/docs/topics/conditional-view-processing.txt deleted file mode 100644 index c631a13..0000000 --- a/parts/django/docs/topics/conditional-view-processing.txt +++ /dev/null @@ -1,199 +0,0 @@ -=========================== -Conditional View Processing -=========================== - -.. versionadded:: 1.1 - -HTTP clients can send a number of headers to tell the server about copies of a -resource that they have already seen. This is commonly used when retrieving a -Web page (using an HTTP ``GET`` request) to avoid sending all the data for -something the client has already retrieved. However, the same headers can be -used for all HTTP methods (``POST``, ``PUT``, ``DELETE``, etc). - -For each page (response) that Django sends back from a view, it might provide -two HTTP headers: the ``ETag`` header and the ``Last-Modified`` header. These -headers are optional on HTTP responses. They can be set by your view function, -or you can rely on the :class:`~django.middleware.common.CommonMiddleware` -middleware to set the ``ETag`` header. - -When the client next requests the same resource, it might send along a header -such as `If-modified-since`_, containing the date of the last modification -time it was sent, or `If-none-match`_, containing the ``ETag`` it was sent. -If the current version of the page matches the ``ETag`` sent by the client, or -if the resource has not been modified, a 304 status code can be sent back, -instead of a full response, telling the client that nothing has changed. - -.. _If-none-match: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.26 -.. _If-modified-since: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.25 - -When you need more fine-grained control you may use per-view conditional -processing functions. - -.. conditional-decorators: - -The ``condition`` decorator -=========================== - -Sometimes (in fact, quite often) you can create functions to rapidly compute the ETag_ -value or the last-modified time for a resource, **without** needing to do all -the computations needed to construct the full view. Django can then use these -functions to provide an "early bailout" option for the view processing. -Telling the client that the content has not been modified since the last -request, perhaps. - -.. _ETag: http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.11 - -These two functions are passed as parameters the -``django.views.decorators.http.condition`` decorator. This decorator uses -the two functions (you only need to supply one, if you can't compute both -quantities easily and quickly) to work out if the headers in the HTTP request -match those on the resource. If they don't match, a new copy of the resource -must be computed and your normal view is called. - -The ``condition`` decorator's signature looks like this:: - - condition(etag_func=None, last_modified_func=None) - -The two functions, to compute the ETag and the last modified time, will be -passed the incoming ``request`` object and the same parameters, in the same -order, as the view function they are helping to wrap. The function passed -``last_modified_func`` should return a standard datetime value specifying the -last time the resource was modified, or ``None`` if the resource doesn't -exist. The function passed to the ``etag`` decorator should return a string -representing the `Etag`_ for the resource, or ``None`` if it doesn't exist. - -Using this feature usefully is probably best explained with an example. -Suppose you have this pair of models, representing a simple blog system:: - - 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) - ... - -If the front page, displaying the latest blog entries, only changes when you -add a new blog entry, you can compute the last modified time very quickly. You -need the latest ``published`` date for every entry associated with that blog. -One way to do this would be:: - - def latest_entry(request, blog_id): - return Entry.objects.filter(blog=blog_id).latest("published").published - -You can then use this function to provide early detection of an unchanged page -for your front page view:: - - from django.views.decorators.http import condition - - @condition(last_modified_func=latest_entry) - def front_page(request, blog_id): - ... - -Shortcuts for only computing one value -====================================== - -As a general rule, if you can provide functions to compute *both* the ETag and -the last modified time, you should do so. You don't know which headers any -given HTTP client will send you, so be prepared to handle both. However, -sometimes only one value is easy to compute and Django provides decorators -that handle only ETag or only last-modified computations. - -The ``django.views.decorators.http.etag`` and -``django.views.decorators.http.last_modified`` decorators are passed the same -type of functions as the ``condition`` decorator. Their signatures are:: - - etag(etag_func) - last_modified(last_modified_func) - -We could write the earlier example, which only uses a last-modified function, -using one of these decorators:: - - @last_modified(latest_entry) - def front_page(request, blog_id): - ... - -...or:: - - def front_page(request, blog_id): - ... - front_page = last_modified(latest_entry)(front_page) - -Use ``condition`` when testing both conditions ------------------------------------------------- - -It might look nicer to some people to try and chain the ``etag`` and -``last_modified`` decorators if you want to test both preconditions. However, -this would lead to incorrect behavior. - -:: - - # Bad code. Don't do this! - @etag(etag_func) - @last_modified(last_modified_func) - def my_view(request): - # ... - - # End of bad code. - -The first decorator doesn't know anything about the second and might -answer that the response is not modified even if the second decorators would -determine otherwise. The ``condition`` decorator uses both callback functions -simultaneously to work out the right action to take. - -Using the decorators with other HTTP methods -============================================ - -The ``condition`` decorator is useful for more than only ``GET`` and -``HEAD`` requests (``HEAD`` requests are the same as ``GET`` in this -situation). It can be used also to be used to provide checking for ``POST``, -``PUT`` and ``DELETE`` requests. In these situations, the idea isn't to return -a "not modified" response, but to tell the client that the resource they are -trying to change has been altered in the meantime. - -For example, consider the following exchange between the client and server: - - 1. Client requests ``/foo/``. - 2. Server responds with some content with an ETag of ``"abcd1234"``. - 3. Client sends an HTTP ``PUT`` request to ``/foo/`` to update the - resource. It also sends an ``If-Match: "abcd1234"`` header to specify - the version it is trying to update. - 4. Server checks to see if the resource has changed, by computing the ETag - the same way it does for a ``GET`` request (using the same function). - If the resource *has* changed, it will return a 412 status code code, - meaning "precondition failed". - 5. Client sends a ``GET`` request to ``/foo/``, after receiving a 412 - response, to retrieve an updated version of the content before updating - it. - -The important thing this example shows is that the same functions can be used -to compute the ETag and last modification values in all situations. In fact, -you **should** use the same functions, so that the same values are returned -every time. - -Comparison with middleware conditional processing -================================================= - -You may notice that Django already provides simple and straightforward -conditional ``GET`` handling via the -:class:`django.middleware.http.ConditionalGetMiddleware` and -:class:`~django.middleware.common.CommonMiddleware`. Whilst certainly being -easy to use and suitable for many situations, those pieces of middleware -functionality have limitations for advanced usage: - - * They are applied globally to all views in your project - * They don't save you from generating the response itself, which may be - expensive - * They are only appropriate for HTTP ``GET`` requests. - -You should choose the most appropriate tool for your particular problem here. -If you have a way to compute ETags and modification times quickly and if some -view takes a while to generate the content, you should consider using the -``condition`` decorator described in this document. If everything already runs -fairly quickly, stick to using the middleware and the amount of network -traffic sent back to the clients will still be reduced if the view hasn't -changed. - |