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 ConditionalGetMiddleware
middleware to set the ETag header.

When the client next requests the same resource, it might send along a header
such as either If-modified-since or
If-unmodified-since, containing the date of the last
modification time it was sent, or either If-match or
If-none-match, containing the last 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.
Depending on the header, if the page has been modified or does not match the
ETag sent by the client, a 412 status code (Precondition Failed) may be
returned.

When you need more fine-grained control you may use per-view conditional
processing functions.

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.

These two functions are passed as parameters to 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.

The decorator sets the ETag and Last-Modified headers on the response
if they are not already set by the view and if the request’s method is safe
(GET or HEAD).

Using this feature usefully is probably best explained with an example.
Suppose you have this pair of models, representing a small blog system:

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:

When condition() returns a conditional response, any decorators below
it will be skipped and won’t apply to the response. Therefore, any
decorators that need to apply to both the regular view response and a
conditional response must be above condition(). In particular,
vary_on_cookie(),
vary_on_headers(), and
cache_control() should come first
because RFC 7232 requires that the headers they
set be present on 304 responses.

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:

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.

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 also 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:

Client requests /foo/.

Server responds with some content with an ETag of "abcd1234".

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.

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,
meaning “precondition failed”.

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.

Validator headers with non-safe request methods

The condition decorator only sets validator headers (ETag and
Last-Modified) for safe HTTP methods, i.e. GET and HEAD. If you
wish to return them in other cases, set them in your view. See
RFC 7231#section-4.3.4 to learn about the distinction between setting a
validator header in response to requests made with PUT versus POST.

It doesn’t save you from generating the response, which may be expensive.

It’s 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.