Как кэшировать разбитый на страницы запрос Django

Как кэшировать разбитый на страницы Django запрос, особенно в ListView?

Я заметил, что один запрос занимает много времени, поэтому я пытаюсь его кэшировать. Набор запросов огромен (более 100 тыс. Записей), поэтому я пытаюсь только кэшировать разбитые на него подразделы. Я не могу кэшировать весь вид или шаблон, потому что есть разделы, которые специфичны для пользователя / сессии и должны постоянно меняться.

В ListView есть несколько стандартных методов для извлечения get_queryset() , get_queryset() , который возвращает непамицированные данные и paginate_queryset() , который фильтрует его на текущей странице.

Сначала я попытался кэшировать запрос в get_queryset() , но быстро реализованный вызов cache.set(my_query_key, super(MyView, self).get_queryset()) вызывал сериализацию всего запроса.

Итак, я попробовал переопределить paginate_queryset() как:

 import time from functools import partial from django.core.cache import cache from django.views.generic import ListView class MyView(ListView): ... def paginate_queryset(self, queryset, page_size): cache_key = 'myview-queryset-%s-%s' % (self.page, page_size) print 'paginate_queryset.cache_key:',cache_key t0 = time.time() ret = cache.get(cache_key) if ret is None: print 're-caching' ret = super(MyView, self).paginate_queryset(queryset, page_size) cache.set(cache_key, ret, 60*60) td = time.time() - t0 print 'paginate_queryset.time.seconds:',td (paginator, page, object_list, other_pages) = ret print 'total objects:',len(object_list) return ret 

Тем не менее, это занимает почти минуту, чтобы работать, хотя только 10 объектов извлекаются, и каждый запрос показывает «повторное кэширование», подразумевая, что в кеш ничего не сохраняется.

Мои settings.CACHE CACHE выглядит так:

 CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', 'LOCATION': '127.0.0.1:11211', } } 

и service memcached status показывает, что memcached запущен, а tail -f /var/log/memcached.log абсолютно ничего не показывает.

Что я делаю не так? Каким образом можно кэшировать разбитый на страницы запрос, чтобы весь запрос не был получен?

Изменить: я думаю, что это может быть ошибка в memcached или оболочке Python. Кажется, что Django поддерживает два разных memcached-сервера: один использует python-memcached и один использует pylibmc. Кажется, что python-memcached молча скрывает ошибку, paginate_queryset() значение paginate_queryset() . Когда я переключился на бэкэнд pylibmc, теперь я получаю явное сообщение об ошибке «ошибка 10 из memcached_set: SERVER ERROR», переходящее назад в django / core / cache / backends / memcached.py в строке, строка 78.

2 Solutions collect form web for “Как кэшировать разбитый на страницы запрос Django”

Вы можете расширить Paginator для поддержки кэширования с помощью предоставленной cache_key .

Сообщение в блоге об использовании и реализации такого CachedPaginator можно найти здесь . Исходный код размещен на djangosnippets.org (здесь есть ссылка на веб-актер, потому что оригинал не работает).

Однако я опубликую немного модифицированный пример из исходной версии, который может не только кэшировать объекты на странице, но и общее количество. (иногда даже счет может быть дорогостоящей операцией).

 from django.core.cache import cache from django.utils.functional import cached_property from django.core.paginator import Paginator, Page, PageNotAnInteger class CachedPaginator(Paginator): """A paginator that caches the results on a page by page basis.""" def __init__(self, object_list, per_page, orphans=0, allow_empty_first_page=True, cache_key=None, cache_timeout=300): super(CachedPaginator, self).__init__(object_list, per_page, orphans, allow_empty_first_page) self.cache_key = cache_key self.cache_timeout = cache_timeout @cached_property def count(self): """ The original django.core.paginator.count attribute in Django1.8 is not writable and cant be setted manually, but we would like to override it when loading data from cache. (instead of recalculating it). So we make it writable via @cached_property. """ return super(CachedPaginator, self).count def set_count(self, count): """ Override the paginator.count value (to prevent recalculation) and clear num_pages and page_range which values depend on it. """ self.count = count # if somehow we have stored .num_pages or .page_range (which are cached properties) # this can lead to wrong page calculations (because they depend on paginator.count value) # so we clear their values to force recalculations on next calls try: del self.num_pages except AttributeError: pass try: del self.page_range except AttributeError: pass @cached_property def num_pages(self): """This is not writable in Django1.8. We want to make it writable""" return super(CachedPaginator, self).num_pages @cached_property def page_range(self): """This is not writable in Django1.8. We want to make it writable""" return super(CachedPaginator, self).page_range def page(self, number): """ Returns a Page object for the given 1-based page number. This will attempt to pull the results out of the cache first, based on the requested page number. If not found in the cache, it will pull a fresh list and then cache that result + the total result count. """ if self.cache_key is None: return super(CachedPaginator, self).page(number) # In order to prevent counting the queryset # we only validate that the provided number is integer # The rest of the validation will happen when we fetch fresh data. # so if the number is invalid, no cache will be setted # number = self.validate_number(number) try: number = int(number) except (TypeError, ValueError): raise PageNotAnInteger('That page number is not an integer') page_cache_key = "%s:%s:%s" % (self.cache_key, self.per_page, number) page_data = cache.get(page_cache_key) if page_data is None: page = super(CachedPaginator, self).page(number) #cache not only the objects, but the total count too. page_data = (page.object_list, self.count) cache.set(page_cache_key, page_data, self.cache_timeout) else: cached_object_list, cached_total_count = page_data self.set_count(cached_total_count) page = Page(cached_object_list, number, self) return page 

Проблема оказалась комбинацией факторов. В основном, результат, возвращаемый paginate_queryset() содержит ссылку на неограниченный набор запросов, что означает, что он по существу не доступен. Когда я вызывал cache.set(mykey, (paginator, page, object_list, other_pages)) , он пытался сериализовать тысячи записей, а не только page_size которые я ожидал, заставляя кешированный элемент превышать лимиты memcached и терпеть неудачу ,

Другим фактором было ужасное сообщение об ошибке по умолчанию в memcached / python-memcached, которое молча скрывает все ошибки и превращает cache.set () в nop, если что-то пойдет не так, что делает его очень трудоемким для отслеживания проблемы.

Я исправил это, по существу переписывая paginate_queryset() чтобы полностью скомпоновать функциональные возможности paginator Django и вычислить набор запросов самостоятельно:

 object_list = queryset[page_size*(page-1):page_size*(page-1)+page_size] 

а затем кешировать этот object_list .

  • Удаление определенных элементов из кеша Django?
  • Какой хороший аналог Flask / Python / WSGI для общедоступных хранилищ PHP Apache, таких как apc_store / apc_fetch?
  • Как проверить содержимое кеша Django с помощью Python memcached?
  • Обзор кода Django Memcache: Сравнить и установить
  • Обмен Memcache с PHP и Python
  • Как кэшировать результат запроса в django?
  • memcache не будет хранить ключ / значение, потому что значение слишком велико
  • Каков наилучший метод сериализации для объектов в memcached?
  • Как импортировать классы django.middleware в проект Google App Engine?
  • Кэширование Matplotlib с Memcache (Wont Pickle)
  • Как я могу очистить memcache Google App Engine при каждом развертывании сайта?
  •  
    Interesting Posts for Van-Lav

    Поиск значений клавиш со стрелками в Python: почему они являются тройками?

    Сортировка Python по максимуму первого элемента, затем min второго элемента

    если y> 0.0 и x -y> = – Q1: ValueError: значение истинности массива с более чем одним элементом неоднозначно. Используйте команды a.any () или a.all ()

    Распределенный TensorFlow – Не работает некоторые рабочие

    Как изменить размер общей памяти в Python

    Метод last () модели Django

    Лучший способ разбивать необработанный SQL-запрос в представлении списка Django REST ListAPI?

    Преобразование Python в R

    Как я могу отформатировать список для печати каждого элемента в отдельной строке в python?

    Внедрение нечувствительного к акценту поиска на django с использованием sqlite

    Как выполнять несколько задач параллельно в структуре

    Можно ли декомпилировать скомпилированный файл .pyc в файл .py?

    многоразовое приложение для объявлений на сайтах django, которые отображают сообщение только один раз для каждого пользователя

    Flask – ImportError: нет модуля с именем app

    Извлечение текста из файла PDF с помощью PDFMiner в python?

    Python - лучший язык программирования в мире.