Обработка условных представлений
HTTP-клиенты могут отправлять ряд заголовков, чтобы сообщить серверу о копиях ресурса, которые они уже видели. Это обычно используется при получении веб-страницы (используя HTTP-запрос) для предотвращения отправки всех данных для чего-то, что клиент уже получил. Однако те же заголовки могут использоваться для всех HTTP-методов ( , , , и т.д.).
Для каждой страницы (ответа), которую Django отправляет обратно из представления, она может предоставить два HTTP-заголовка: заголовок и заголовок. Эти заголовки являются необязательными в HTTP-ответах. Их можно задать в вашей функции представления, или вы можете положиться на middleware middleware для задания заголовка.
Когда клиент в следующий раз запрашивает тот же ресурс, он может отправить заголовок, например, или , содержащий дату последнего времени изменения, или или , содержащий последнее значение, которое было отправлено. Если текущая версия страницы соответствует значению, отправленному клиентом, или если ресурс не был изменён, можно отправить код состояния 304, вместо полного ответа, сообщив клиенту, что ничего не изменилось. В зависимости от заголовка, если страница была изменена или не соответствует значению, отправленному клиентом, может быть возвращён код состояния 412 (Ошибка предварительного условия).
Если вам нужен более точный контроль, вы можете использовать функции условной обработки на уровне представлений.
Поддержка заголовка была добавлена к обработке условных представлений.
Декоратор
Иногда (на самом деле, довольно часто) вы можете создавать функции для быстрого вычисления значения ETag или времени последнего изменения ресурса, **без** необходимости выполнения всех вычислений, необходимых для построения полного представления. Django затем может использовать эти функции для предоставления варианта «быстрого выхода» для обработки представления. Сообщая клиенту, что содержимое не изменялось с момента последнего запроса, возможно.
Эти две функции передаются в качестве параметров декоратору . Этот декоратор использует две функции (вам нужно указать только одну, если вы не можете легко и быстро вычислить оба значения) для определения того, соответствуют ли заголовки в HTTP-запросе заголовкам ресурса. Если они не совпадают, необходимо вычислить новую копию ресурса, и будет вызвана ваша обычная функция представления.
Подпись декоратора выглядит так:
condition(etag_func=None, last_modified_func=None)
Две функции для вычисления ETag и времени последнего изменения будут переданы объект входящего запроса и те же параметры в том же порядке, что и функция представления, которую они помогают обернуть. Функция, переданная , должна возвращать стандартное значение datetime, указывающее последнее время изменения ресурса, или , если ресурс не существует. Функция, переданная декоратору , должна возвращать строку, представляющую Etag ресурса, или , если он не существует.
Использование этой функции наиболее эффективно объясняется на примере. Предположим, у вас есть эта пара моделей, представляющих простую систему блога:
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)
...
Если главная страница, отображающая последние записи блога, меняется только при добавлении новой записи блога, вы можете быстро вычислить время последнего изменения. Вам нужно последнее значение даты для каждой записи, связанной с этим блогом. Один из способов сделать это:
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):
...
Сокращения для вычисления только одного значения
Как общее правило, если вы можете предоставить функции для вычисления обоих значений ETag и времени последнего изменения, вы должны это сделать. Вы не знаете, какие заголовки любой данный HTTP-клиент вам отправит, поэтому будьте готовы обработать оба. Однако иногда вычислить легко только одно значение, и Django предоставляет декораторы, обрабатывающие только вычисления ETag или только времени последнего изменения.
Декораторы и получают те же типы функций, что и декоратор . Их подписи такие:
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)
Использовать , когда проверяются оба условия
Некоторым может показаться более удобным попытаться связать декораторы и , если вы хотите проверить оба предварительных условия. Однако это приведёт к неправильному поведению.
# Bad code. Don't do this!
@etag(etag_func)
@last_modified(last_modified_func)
def my_view(request):
# ...
# End of bad code.
Первый декоратор ничего не знает о втором и может ответить, что ответ не изменён, даже если второй декоратор определит обратное. Декоратор использует обе функции обратного вызова одновременно, чтобы определить правильное действие.
Использование декораторов с другими HTTP-методами
Декоратор полезен не только для запросов и запросов (запросы такие же, как запросы в этой ситуации). Он также может использоваться для проверки запросов, запросов и запросов. В этих ситуациях идея не в том, чтобы вернуть ответ «не изменён», а в том, чтобы сообщить клиенту, что ресурс, который он пытается изменить, был изменён тем временем.
Например, рассмотрим следующий обмен между клиентом и сервером:
- Клиент запрашивает
- Сервер отвечает некоторым содержимым с ETag
- Клиент отправляет HTTP-запрос на обновление ресурса. Он также отправляет заголовок , чтобы указать версию, которую он пытается обновить.
- Сервер проверяет, изменился ли ресурс, вычислив ETag так же, как и для запроса (используя ту же функцию). Если ресурс изменился, он вернёт код состояния 412, что означает «ошибка предварительного условия».
- Клиент отправляет запрос на получение обновлённой версии содержимого перед обновлением.
Важное наблюдение этого примера заключается в том, что одни и те же функции могут использоваться для вычисления значений ETag и последнего изменения во всех ситуациях. На самом деле, вы должны использовать одни и те же функции, чтобы каждый раз возвращались одни и те же значения.
Сравнение с условной обработкой middleware
Вы можете заметить, что Django уже предоставляет простую и понятную обработку условных действий через middleware middleware и middleware. Хотя они очень просты в использовании и подходят для многих ситуаций, эти фрагменты функциональности middleware имеют ограничения для продвинутого использования:
- Они применяются глобально ко всем представлениям в вашем проекте
- Они не экономят вас от создания самого ответа, что может быть дорогостоящим
- Они подходят только для HTTP-запросов.
Вы должны выбрать наиболее подходящий инструмент для вашей конкретной проблемы. Если у вас есть способ быстро вычислить ETag и время изменения, и если какое-то представление занимает много времени для генерации содержимого, вы должны рассмотреть использование декоратора, описанного в этом документе. Если всё выполняется достаточно быстро, используйте middleware, и количество сетевого трафика, отправленного клиентам, всё равно будет уменьшено, если представление не изменилось.
© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/1.9/topics/conditional-view-processing/