Условный View Processing ¶
HTTP-клиенты могут отправлять несколько заголовков, чтобы сообщить серверу о копиях ресурса, которые они уже видели. Это обычно используется при получении веб-страницы (с помощью HTTP- GET
запроса), чтобы избежать отправки всех данных о том, что клиент уже получил. Тем не менее, одни и те же заголовки могут быть использованы для всех методов HTTP ( POST
, PUT
, DELETE
и т.д.).
Для каждой страницы (ответа), которую Django отправляет обратно из представления, он может предоставить два заголовка HTTP: ETag
заголовок и Last-Modified
заголовок. Эти заголовки необязательны для HTTP-ответов. Они могут быть установлены вашей функцией просмотра, или вы можете полагаться на ConditionalGetMiddleware
промежуточное программное обеспечение для установки ETag
заголовка.
Когда клиент в следующий раз запрашивает тот же ресурс, он может отправить заголовок, например Если-изменено-с или
If-unmodified-Since , содержащий дату последней модификации, когда оно было отправлено, или либоЕсли совпадение или
If-none-match , содержащий последнийETag
отправленный. Если текущая версия страницы совпадает сETag
отправленной клиентом, или если ресурс не был изменен, код состояния 304 может быть отправлен обратно вместо полного ответа, сообщая клиенту, что ничего не изменилось. В зависимости от заголовка, если страница была изменена или не соответствует
ETag
отправленной клиентом, может быть возвращен код состояния 412 (Precondition Failed).
Когда вам нужен более детальный контроль, вы можете использовать функции условной обработки для каждого представления.
condition
Декоратор ¶
Иногда (на самом деле, довольно часто) вы можете создавать функции для быстрого вычисления значения ETag или времени последнего изменения для ресурса без необходимости выполнять все вычисления, необходимые для построения полного представления. Затем Django может использовать эти функции для предоставления возможности «раннего аварийного выхода» для обработки представления. Возможно, сообщив клиенту, что контент не изменялся с момента последнего запроса.
Эти две функции передаются django.views.decorators.http.condition
декоратору в качестве параметров
. Этот декоратор использует две функции (вам нужно указать только одну, если вы не можете легко и быстро вычислить обе величины), чтобы определить, совпадают ли заголовки в HTTP-запросе с заголовками ресурса. Если они не совпадают, необходимо вычислить новую копию ресурса и вызвать ваше обычное представление.
В condition
подписи декоратора выглядит следующим образом :
condition(etag_func=None, last_modified_func=None)
Обеим функциям для вычисления ETag и времени последнего изменения будут переданы входящий request
объект и те же параметры в том же порядке, что и функции представления, которую они помогают обернуть. Переданная функция
last_modified_func
должна возвращать стандартное значение даты и времени, указывающее, когда в последний раз ресурс был изменен, или None
если ресурс не существует. Функция, переданная etag
декоратору, должна возвращать строку, представляющую ETag для ресурса, или, None
если он не существует.
Декоратор устанавливает заголовки ETag
и Last-Modified
в ответе, если они еще не установлены представлением и если метод запроса безопасен ( GET
или HEAD
).
Использование этой функции с пользой, вероятно, лучше всего пояснить на примере. Предположим, у вас есть эта пара моделей, представляющих небольшую систему блогов:
import datetime
from django.db import models
class Blog(models.Model):
...
class Entry(models.Model):
blog = models.ForeignKey(Blog, on_delete=models.CASCADE)
published = models.DateTimeField(default=datetime.datetime.now)
...
Если первая страница, отображающая последние записи блога, изменяется только при добавлении новой записи блога, вы можете очень быстро вычислить время последнего изменения. Вам нужна самая последняя published
дата для каждой записи, связанной с этим блогом. Один из способов сделать это:
def latest_entry(request, blog_id):
return Entry.objects.filter(blog=blog_id).latest("published").published
Затем вы можете использовать эту функцию, чтобы обеспечить раннее обнаружение неизмененной страницы для просмотра вашей первой страницы:
from django.views.decorators.http import condition
@condition(last_modified_func=latest_entry)
def front_page(request, blog_id):
...
Будьте осторожны с порядком декораторов
При condition()
возврате условного ответа любые декораторы ниже него будут пропущены и не будут применяться к ответу. Следовательно, любые декораторы, которые должны применяться как к обычному ответу представления, так и к условному ответу, должны быть указаны выше condition()
. В частности,
vary_on_cookie()
,
vary_on_headers()
и
cache_control()
должен прийти первым , потому чтоRFC 7232 требует, чтобы установленные ими заголовки присутствовали в ответах 304.
Ярлыки для вычисления только одного значения ¶
Как правило, если вы можете предоставить функции для вычисления как ETag, так и времени последнего изменения, вы должны это сделать. Вы не знаете, какие заголовки отправит вам какой-либо конкретный HTTP-клиент, поэтому будьте готовы обработать и то, и другое. Однако иногда легко вычислить только одно значение, и Django предоставляет декораторы, которые обрабатывают только ETag или только вычисления с последними изменениями.
django.views.decorators.http.etag
И
django.views.decorators.http.last_modified
декораторы прошли тот же тип функций , как condition
декоратор. Их подписи:
etag(etag_func)
last_modified(last_modified_func)
Мы могли бы написать предыдущий пример, в котором используется только последняя измененная функция, используя один из этих декораторов:
@last_modified(latest_entry)
def front_page(request, blog_id):
...
…или же:
def front_page(request, blog_id):
...
front_page = last_modified(latest_entry)(front_page)
Используйте condition
при тестировании обоих условий ¶
Это может выглядеть лучше для некоторых людей , чтобы попытаться ЦЕПЬ etag
и
last_modified
декораторов , если вы хотите проверить , как предварительные условия. Однако это приведет к неправильному поведению.
# Bad code. Don't do this!
@etag(etag_func)
@last_modified(last_modified_func)
def my_view(request):
# ...
# End of bad code.
Первый декоратор ничего не знает о втором и может ответить, что ответ не изменяется, даже если вторые декораторы определят иначе. condition
Декоратор использует обе функции обратного вызова одновременно выработать правильные действия предпринять.
Используя декоратор с другими методами HTTP ¶
condition
Декоратор полезно для более чем только GET
и
HEAD
запросов ( HEAD
запросы на такие же , как GET
в такой ситуации). Он также может быть использован для обеспечения проверки для POST
,
PUT
и DELETE
запросов. В этих ситуациях идея заключается не в том, чтобы вернуть ответ «не изменен», а в том, чтобы сообщить клиенту, что ресурс, который они пытаются изменить, тем временем был изменен.
Например, рассмотрим следующий обмен между клиентом и сервером:
- Клиентские запросы
/foo/
. - Сервер отвечает неким контентом с ETag
"abcd1234"
. - Клиент отправляет HTTP-
PUT
запрос/foo/
на обновление ресурса. Он также отправляет заголовок, чтобы указать версию, которую он пытается обновить.If-Match: "abcd1234"
- Сервер проверяет, изменился ли ресурс, вычисляя ETag так же, как и для
GET
запроса (используя ту же функцию). Если ресурс был изменен, он будет возвращать код 412 статуса, что означает «предварительное условие не удалось». - После получения ответа 412 клиент отправляет
GET
запрос на/foo/
получение обновленной версии контента перед ее обновлением.
Важная вещь, которую показывает этот пример, заключается в том, что одни и те же функции могут использоваться для вычисления значений ETag и последней модификации во всех ситуациях. Фактически, вы должны использовать одни и те же функции, чтобы каждый раз возвращались одни и те же значения.
Заголовки валидатора с небезопасными методами запроса
condition
Декоратор устанавливает только валидаторы заголовки ( ETag
и
Last-Modified
) для безопасных методов HTTP, то есть GET
и HEAD
. Если вы хотите вернуть их в других случаях, установите их в своем представлении. Видеть
RFC 7231 # section-4.3.4, чтобы узнать о различиях между установкой заголовка валидатора в ответ на запросы, сделанные с помощьюPUT
versusPOST
.
Сравнение с условной обработкой промежуточного ПО ¶
Django обеспечивает условную GET
обработку через
django.middleware.http.ConditionalGetMiddleware
. Хотя промежуточное ПО подходит для многих ситуаций, оно имеет ограничения для расширенного использования:
- Он применяется глобально ко всем представлениям в вашем проекте.
- Это не избавляет вас от генерации ответа, который может быть дорогостоящим.
- Это подходит только для HTTP-
GET
запросов.
Здесь вы должны выбрать наиболее подходящий инструмент для вашей конкретной проблемы. Если у вас есть способ быстро вычислить ETags и время модификации и если какое-то представление требует времени для генерации контента, вам следует рассмотреть возможность использования
condition
декоратора, описанного в этом документе. Если все уже работает довольно быстро, придерживайтесь промежуточного программного обеспечения, и объем сетевого трафика, отправляемого обратно клиентам, все равно будет уменьшен, если представление не изменилось.