Spec-Zone.ru › Django 1.8

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

Клиенты 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 (Предварительное условие не выполнено).

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

Поддержка заголовка If-unmodified-since была добавлена к обработке условных представлений.

Декоратор 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):
    ...

Сокращения для вычисления только одного значения

Как общее правило, если вы можете предоставить функции для вычисления обоих значений 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.8/topics/conditional-view-processing/

Spec-Zone.ru

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