Spec-Zone.ru › Django 1.10

Обработка условного отображения

Клиенты HTTP могут отправлять множество заголовков, чтобы сообщить серверу о копиях ресурса, которые они уже видели. Это обычно используется при получении веб-страницы (используя запрос HTTP GET ), чтобы избежать отправки всех данных для чего-то, что клиент уже загрузил. Однако те же заголовки могут использоваться для всех методов HTTP (POST, PUT, DELETE, и т. д.).

Для каждой страницы (ответа), которую Django возвращает из представления, он может предоставить два заголовка HTTP: заголовок ETag и заголовок Last-Modified. Эти заголовки являются необязательными в ответах HTTP. Их можно установить в вашей функции представления, или вы можете полагаться на middleware CommonMiddleware, чтобы установить заголовок ETag.

При следующем запросе клиента к тому же ресурсу он может отправить заголовок, например, If-modified-since или If-unmodified-since, содержащий дату последнего времени изменения, или If-match или If-none-match, содержащий последнее ETag , которое было отправлено. Если текущая версия страницы соответствует значению ETag , отправленному клиентом, или если ресурс не был изменён, может быть отправлен код состояния 304, вместо полного ответа, сообщая клиенту, что ничего не изменилось. В зависимости от заголовка, если страница была изменена или не соответствует значению ETag , отправленному клиентом, может быть возвращён код состояния 412 (Ошибка предварительного условия).

Если вам требуется более точный контроль, вы можете использовать функции обработки условных представлений на уровне представления.

Декоратор condition

Иногда (на самом деле довольно часто) вы можете создать функции для быстрого вычисления значения ETag или последнего времени изменения ресурса, **без** необходимости выполнять все вычисления, необходимые для построения полного представления. Django затем может использовать эти функции для предоставления опции «преждевременного выхода» для обработки представления. Сообщая клиенту, что содержимое не изменялось с момента последнего запроса, возможно.

Эти две функции передаются в качестве параметров декоратору django.views.decorators.http.condition. Этот декоратор использует две функции (вам нужно предоставить только одну, если вы не можете легко и быстро вычислить оба значения), чтобы выяснить, соответствуют ли заголовки в запросе HTTP заголовкам ресурса. Если они не совпадают, новая копия ресурса должна быть вычислена, и будет вызвано ваше обычное представление.

Подпись декоратора condition выглядит следующим образом:

condition(etag_func=None, last_modified_func=None)

Две функции для вычисления ETag и последнего времени изменения будут переданы объект входящего request и те же параметры, в том же порядке, что и функции представления, которую они помогают обернуть. Функция, передаваемая last_modified_func , должна возвращать стандартное значение datetime, указывающее последнее время изменения ресурса, или None , если ресурс не существует. Функция, передаваемая декоратору etag , должна возвращать строку, представляющую ETag ресурса или None , если он не существует.

Использование этой функции наиболее эффективно демонстрируется на примере. Предположим, у вас есть пара моделей, представляющих простую систему блога:

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)
    ...

Если главная страница, отображающая последние записи блога, изменяется только при добавлении новой записи блога, вы можете очень быстро вычислить время последнего изменения. Вам нужно последнее 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. В этих ситуациях идея не в том, чтобы вернуть ответ «неизменённый», а в том, чтобы сообщить клиенту, что ресурс, который он пытается изменить, тем временем был изменён.

Например, рассмотрите следующий обмен между клиентом и сервером:

  1. Клиент запрашивает /foo/.
  2. Сервер отвечает некоторым содержимым с ETag "abcd1234".
  3. Клиент отправляет запрос HTTP PUT на /foo/ для обновления ресурса. Он также отправляет заголовок If-Match: "abcd1234" , чтобы указать версию, которую он пытается обновить.
  4. Сервер проверяет, изменился ли ресурс, вычислив ETag так же, как для запроса GET (используя ту же функцию). Если ресурс изменился, он вернёт код состояния 412, означающий «ошибка предварительного условия».
  5. Клиент отправляет запрос GET на /foo/, после получения ответа 412, для получения обновлённой версии содержимого перед обновлением.

Важно, что этот пример показывает, что те же функции могут использоваться для вычисления значений ETag и последнего изменения во всех ситуациях. На самом деле, вы должны использовать те же функции, чтобы каждый раз возвращались одни и те же значения.

Сравнение с обработкой условных запросов с помощью middleware

Вы можете заметить, что Django уже предоставляет простую и понятную обработку условных GET запросов через django.middleware.http.ConditionalGetMiddleware и CommonMiddleware. Хотя они действительно просты в использовании и подходят для многих ситуаций, эти функциональные возможности middleware имеют ограничения для продвинутого использования:

  • Они применяются глобально ко всем представлениям в вашем проекте
  • Они не избавляют вас от генерации ответа, что может быть дорогостоящим
  • Они подходят только для запросов HTTP GET.

Вам нужно выбрать наиболее подходящий инструмент для вашей конкретной задачи. Если у вас есть способ быстро вычислить ETag и время последнего изменения, и если для генерации содержимого какого-то представления требуется много времени, то вы должны рассмотреть использование декоратора condition , описанного в этом документе. Если всё уже выполняется достаточно быстро, продолжайте использовать middleware, и объём сетевого трафика, отправляемого клиентам, всё равно будет уменьшен, если представление не изменилось.

© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/1.10/topics/conditional-view-processing/

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API