Декоратор last_modified
Декоратор last_modified применяется к функциям представления и позволяет серверу возвращать ответ 304 Not Modified, если запрошенный ресурс не изменился с момента последнего визита клиента. В качестве аргумента декоратор принимает функцию обратного вызова, которая должна возвращать объект типа datetime с датой последнего изменения ресурса.
Синтаксис
from django.views.decorators.http import last_modified
@last_modified(get_last_modified)
def my_view(request):
# Код представления
return render(request, 'template.html', context)
Декоратор принимает один обязательный аргумент - функцию, которая вычисляет дату последнего изменения. Эта функция должна принимать те же аргументы, что и представление, и возвращать объект datetime или None, если дата изменения неизвестна.
Пример
Рассмотрим простой пример использования декоратора для представления, которое показывает последнюю статью:
from datetime import datetime
from django.shortcuts import render
from django.views.decorators.http import last_modified
from myapp.models import Article
def get_last_modified(request):
last_article = Article.objects.latest('updated_at')
return last_article.updated_at
@last_modified(get_last_modified)
def latest_article(request):
article = Article.objects.latest('created_at')
return render(request, 'article.html', {'article': article})
Когда клиент запрашивает страницу в первый раз, сервер возвращает полный ответ. При повторных запросах браузер отправляет заголовок If-Modified-Since с датой из предыдущего ответа. Декоратор сравнивает эту дату с датой последнего изменения ресурса и, если дата не изменилась, возвращает ответ 304 Not Modified.
Пример
Давайте используем декоратор с дополнительной проверкой пользовательских данных:
from datetime import datetime
from django.shortcuts import get_object_or_404, render
from django.views.decorators.http import last_modified
from myapp.models import Article, UserProfile
def get_last_modified(request, article_id):
article = get_object_or_404(Article, id=article_id)
last_update = article.updated_at
if request.user.is_authenticated:
profile = get_object_or_404(UserProfile, user=request.user)
if profile.last_activity > last_update:
last_update = profile.last_activity
return last_update
@last_modified(get_last_modified)
def article_detail(request, article_id):
article = get_object_or_404(Article, id=article_id)
return render(request, 'article_detail.html', {'article': article})
Результат выполнения кода при повторном запросе без изменений:
"Response status: 304 Not Modified"
В этом примере функция get_last_modified учитывает не только дату обновления статьи, но и время последней активности пользователя. Если пользователь изменил свой профиль, то контент страницы считается изменённым, даже если сама статья не обновлялась.
Пример
Декоратор last_modified можно комбинировать с декоратором etag для более точного контроля кеширования:
from datetime import datetime
from django.shortcuts import render
from django.views.decorators.http import last_modified, etag
from myapp.models import Article
def get_last_modified(request):
last_article = Article.objects.latest('updated_at')
return last_article.updated_at
def get_etag(request):
last_article = Article.objects.latest('updated_at')
return f"article-{last_article.id}-{last_article.updated_at.timestamp()}"
@last_modified(get_last_modified)
@etag(get_etag)
def article_list(request):
articles = Article.objects.filter(status='published')
return render(request, 'articles.html', {'articles': articles})
При совместном использовании обоих декораторов сервер сначала проверяет дату последнего изменения, а затем ETag. Это обеспечивает максимальную гибкость в управлении кешированием, так как ETag может учитывать дополнительные факторы, не связанные напрямую с датой изменения.
Пример
Если ресурс может быть изменён в зависимости от пользовательских параметров, таких как язык или регион, можно включить эти параметры в вычисление даты изменения:
from datetime import datetime
from django.shortcuts import render
from django.views.decorators.http import last_modified
from myapp.models import Article, Category
def get_last_modified(request, category_slug):
category = get_object_or_404(Category, slug=category_slug)
articles = Article.objects.filter(category=category)
last_article = articles.latest('updated_at')
return last_article.updated_at
@last_modified(get_last_modified)
def category_articles(request, category_slug):
category = get_object_or_404(Category, slug=category_slug)
articles = Article.objects.filter(category=category, status='published')
return render(request, 'category.html', {
'category': category,
'articles': articles,
})
Результат выполнения кода при запросе категории без изменений:
"Status: 304 Not Modified"
"Content loaded from browser cache"
В этом примере каждая категория имеет свою собственную дату последнего изменения, основанную на самой свежей статье в этой категории. Это позволяет эффективно кешировать страницы для каждой категории отдельно.
Смотрите также
-
декоратор
etag,
который управляет кешированием через ETag -
декоратор
condition,
который объединяет проверки по ETag и дате изменения -
декоратор
require_GET,
который ограничивает доступ к представлению только GET-запросами -
декоратор
gzip_page,
который сжимает ответ сервера для экономии трафика