Spec-Zone.ru › Django 3.0

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

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

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

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

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

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

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

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

Spec-Zone.ru

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