Как сделать недействительной cache_page в Django?

Вот проблема: у меня есть приложение блога, и я кеширую представление пост-вывода в течение 5 минут.

@cache_page(60 * 5)
def article(request, slug):
    ...

Тем не менее, я хотел бы сделать кеш недействительным всякий раз, когда в сообщение добавляется новый комментарий. Мне интересно, как лучше это сделать?

Я видел этот связанный вопрос, но он устарел.

3 ответа

Это был первый удар для меня при поиске решения, и текущий ответ не был очень полезным, поэтому после долгих поисков источника Django у меня есть ответ на этот вопрос.

Да, вы можете узнать ключ программно, но это требует небольшой работы.

Кэширование страниц Django работает, ссылаясь наrequestобъект, в частности путь запроса и строка запроса. Это означает, что для каждого запроса к вашей странице с другой строкой запроса у вас будет другой ключ кэша . В большинстве случаев это вряд ли будет проблемой, поскольку страница, которую вы хотите кэшировать/аннулировать, будет известной строкой, например/blog/my-awesome-year, поэтому, чтобы аннулировать это, вам просто нужно использовать DjangoRequestFactory:

      from django.core.cache import cache
from django.test import RequestFactory
from django.urls import reverse
from django.utils.cache import get_cache_key


cache.delete(get_cache_key(RequestFactory().get("/blog/my-awesome-year")))

Если ваши URL-адреса представляют собой фиксированный список значений (т. е. нет различающихся строк запроса), вы можете остановиться здесь. Однако, если у вас много разных строк запроса (например,?q=xyzдля страницы поиска или чего-то еще), то лучше всего создать отдельный кеш для каждого представления. Тогда вы можете просто пройтиcache="cachename"кcache_page()а затем очистите весь кеш с помощью:

      from django.core.cache import caches


caches["my_cache_name"].clear()

Важное примечание об этой тактике

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

Я бы кешировал немного по-другому:

def article(request, slug):
    cached_article = cache.get('article_%s' % slug)
    if not cached_article:
        cached_article = Article.objects.get(slug=slug)
        cache.set('article_%s' % slug, cached_article, 60*5)

    return render(request, 'article/detail.html', {'article':cached_article})

затем сохраняем новый комментарий к этому объекту статьи:

# ...
# add the new comment to this article object, then
if cache.get('article_%s' % article.slug): 
    cache.delete('article_%s' % article.slug)
# ...

Углубившись в код, становится совершенно ясно, что мы можем это сделать, но нам нужен URL-адрес API, который вы хотите сделать недействительным (с запросом и заголовками), что выглядит невозможным, но если да, то Django использует md5 для хэш-ключей, которые вы хотите можно попытаться сгенерировать тот же хеш, используя тот же URL-адрес с заголовками.

      def _generate_cache_key(request, method, headerlist, key_prefix):
"""Return a cache key from the headers given in the header list."""
ctx = hashlib.md5()
for header in headerlist:
    value = request.META.get(header)
    if value is not None:
        ctx.update(value.encode())
url = hashlib.md5(iri_to_uri(request.build_absolute_uri()).encode('ascii'))
cache_key = 'views.decorators.cache.cache_page.%s.%s.%s.%s' % (
    key_prefix, method, url.hexdigest(), ctx.hexdigest())
return _i18n_cache_key_suffix(request, cache_key)

Это выглядит не очень просто: вы можете установить префикс для каждого имеющегося у вас URL-адреса, найти все ключи с одним и тем же префиксом и удалить их один за другим, что является гораздо лучшим вариантом.

      cache_page(DEFAULT_TIMEOUT, None, 'your-cache-prefix')

Я не рекомендую менять параметры запроса каждый раз, когда вы хотите отобразить некэшированные данные, но да, есть и другой способ: вы можете передать заголовок без кэша, когда вам нужны свежие данные. Это будет работать по умолчанию.

Просто примечание

Отсутствие кэша заголовка, а также изменение параметров запроса никогда не сделают недействительным ваш фактический кеш.

Другие вопросы по тегам