Django 1.8 official document translation: 14-1 on demand content processing

Time:2021-3-2

On demand content processing

HTTP clients may send protocol headers to tell the server which resources they have seen. This is when getting the web page (using HTTP)GETRequest) is very common to avoid sending the complete data already obtained by the client. However, the same protocol header can be used for all HTTP methods(POST, PUT, DELETE, and others).

For each page (response) sent back by Django from the view, two HTTP protocol headers are provided:ETagandLast-Modified. These protocol headers are optional in the HTTP response. They can be set by your view functions, or you can rely on themCommonMiddlewareMiddlewareETagAgreement header.

When your client requests the same resource again, it may sendIf-modified-sinceperhapsIf-unmodified-sinceThe protocol header containing the last modification time previously sent; orIf-matchorIf-none-matchProtocol header, containing the previously sentETag. If the current version of the page matches theETagOr if the resource has not been modified, a 304 status code will be sent back instead of a complete reply to tell the client that there is no modification. According to the protocol header, if the page is modified or does not match the message sent by the clientETag, 412 (precondition failed) status code will be returned.

When you need more refined control, you can use the on-demand processing function of each view.

Changed in Django 1.8:

Add to on demand view processingIf-unmodified-sinceSupport of protocol header

The condition

Sometimes (actually often), you can create functions to quickly calculate the cost of resourcesETagValue or last modified time,notYou need to perform all the steps required to build a full view. Django can use these functions to provide an “early bailout” option for view processing. To tell the client that the content has not changed since the last request.

These two functions are passed as arguments to thedjango.views.decorators.http.conditionIn the decorator. This installation uses these two functions (if you can’t calculate it quickly and easily, you only need to provide one) to find out whether the protocol header in the HTTP request matches those resources. If they don’t match, a new copy of the resource is generated and your normal view is called.

conditionThe signature of the decorator is I:

condition(etag_func=None, last_modified_func=None)

The two functions that calculate the last modification time of Etag will be passed in the same orderrequestObjects and the same parameters, just like the view functions they encapsulate.last_modified_funcThe function should return a standard datetime value, which specifies the last time of resource modification, or the resource does not existNone. Pass on toetagThe decorator’s function should return a representation of the resourceEtagOr when the resource does not existNone

It’s a good example of how to use this feature. Suppose you have these two models, which represent 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 shows the last blog post and only changes it when you add a new post, you can calculate the last modification time very quickly. You need the end of every post on this blogreleaseDate. One way to achieve it is to:

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

Next, you can use this function to detect the unmodified page in advance for your front page view

from django.views.decorators.http import condition

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

Shortcut to calculate only one value

A general principle is that if you provide a computational Etag_ And_ Finally, to modify the time function, you should do this: you don’t know which protocol header the HTTP client will send you, so you should be prepared to handle two situations. However, sometimes only one of the two is easy to calculate, and Django only provides you with a decorator to calculate Etag or last modified date.

django.views.decorators.http.etaganddjango.views.decorators.http.last_modifiedAsconditionDecorator, passing in functions of the same type. Their signature is:

etag(etag_func)
last_modified(last_modified_func)

We can write an early example that just uses the function of the last modified date, 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

If you want to test two prerequisites, putetagandlast_modifiedThe ornaments look nice chained together. However, this can 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 later and may send an “unmodified” response, even if the second decorator handles something else.conditionThe decorator also uses two callback functions to figure out which is the right behavior.

Using decorators with other HTTP methods

conditionDecorators are not just forGETandHEADThe request is useful(HEADRequest in this case andGETSame). It can also be used forPOST, PUTandDELETERequest for inspection. In these cases, it is not to return a “not modified (314)” response, but to tell the server that the resource they are trying to modify has been modified during this period.

For example, consider the following interactions between the client and the server:

  1. client request/foo/

  2. The server replies with"abcd1234"Etag content.

  3. Client sends httpPUTRequest to/foo/To update resources. Also sentIf-Match: "abcd1234"Protocol header to specify the version to try to update.

  4. The server checks to see if the resource has been modifiedGETEtag is calculated in the same way as on (using the same function). If resourcesalreadyAfter modification, 412 status code will be returned, which means “precondition failed”.

  5. After receiving the 412 response, the client sends theGETRequest to/foo/To get a new version of the content before updating.

The important thing is that this example shows that Etag and last modification time values are calculated using the same function in all cases. Actually, youshouldUse the same function so that the same value is returned each time.

Use middleware to process on demand

You may notice that Django has passeddjango.middleware.http.ConditionalGetMiddlewareandCommonMiddleware. provides simple and directGETOn demand. These middleware are easy to use and can be used in many situations. However, their functions have some limitations in advanced usage

  • They are used globally for all views in your project.

  • They don’t take the place of you generating the response itself, which can be costly.

  • They only apply to httpGETRequest.

Here, you should choose the tool most suitable for your specific problem. If you have a way to quickly calculate Etag and modification time, and if some views need some time to generate content, you should consider using the method described in this documentconditionAdorner. If some of them are executed very fast, insisting on using middleware will also reduce the network traffic sent back to the client if the view is not modified.

translator:Django document collaborative translation teamOriginal text:Conditional content processing

This paper is based onCC BY-NC-SA 3.0Please keep the author’s signature and the source of the article.

Django document collaborative translation teamWe are short of staff. Interested friends can join us. It is totally public welfare. Communication group: 467338606.