Spec-Zone.ru › Django 2.1

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

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

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

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

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

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

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

Заголовки валидатора с методами запроса, не являющимися безопасными

Декоратор condition устанавливает только заголовки валидатора (ETag и Last-Modified) для безопасных методов HTTP, то есть GET и HEAD. Если вы хотите вернуть их в других случаях, установите их в своём представлении. См. RFC 7231#section-4.3.4, чтобы узнать о различии между установкой заголовка валидатора в ответ на запросы, выполненные с использованием PUT , и POST.

Сравнение с условной обработкой на уровне middleware

Django предоставляет простую и понятную обработку условного GET через django.middleware.http.ConditionalGetMiddleware. Несмотря на простоту использования и пригодность для многих ситуаций, middleware имеет ограничения для расширенного использования:

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

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

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

Spec-Zone.ru

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