Spec-Zone.ru › Django 2.0

Обработка условных представлений

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

Изменено в Django 1.11:

В более старых версиях возвращаемое значение из etag_func() интерпретировалось как необрамленная часть ETag. Это препятствовало использованию слабых ETag, имеющих формат W/"<string>". Теперь ожидается, что возвращаемое значение будет ETag, как определено спецификацией (включая кавычки), хотя для обратной совместимости также принимается необрамленный формат.

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

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.0/topics/conditional-view-processing/

Spec-Zone.ru

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