Created
March 17, 2016 14:45
-
-
Save Cloudo/105a5768336e6b039373 to your computer and use it in GitHub Desktop.
Django Paginators
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from django.core.cache import caches | |
from django.core.paginator import Paginator | |
from django.db import connections | |
class LargeTablePaginator(Paginator): | |
""" | |
Overrides the count method to get an estimate instead of actual count when not filtered | |
""" | |
def _get_count(self): | |
""" | |
Changed to use an estimate if the estimate is greater than 10,000 | |
Returns the total number of objects, across all pages. | |
""" | |
if self._count is None: | |
try: | |
estimate = 0 | |
if not self.object_list.query.where: | |
try: | |
cursor = connections[self.object_list.db].cursor() | |
cursor.execute("SELECT reltuples FROM pg_class WHERE relname = %s", | |
[self.object_list.query.model._meta.db_table]) | |
estimate = int(cursor.fetchone()[0]) | |
except: | |
pass | |
if estimate < 10000: | |
self._count = self.object_list.count() | |
else: | |
self._count = estimate | |
except (AttributeError, TypeError): | |
# AttributeError if object_list has no count() method. | |
# TypeError if object_list.count() requires arguments | |
# (i.e. is of type list). | |
self._count = len(self.object_list) | |
return self._count | |
count = property(_get_count) | |
class CachingPaginator(Paginator): | |
''' | |
A custom paginator that helps to cut down on the number of | |
SELECT COUNT(*) form table_name queries. These are really slow, therefore | |
once we execute the query, we will cache the result which means the page | |
numbers are not going to be very accurate but we don't care | |
''' | |
def _get_count(self): | |
""" | |
Returns the total number of objects, across all pages. | |
""" | |
cache = caches['default'] | |
if self._count is None: | |
try: | |
key = "adm:{0}:count".format( hash(self.object_list.query.__str__()) ) | |
self._count = cache.get(key, -1) | |
if self._count == -1 : | |
if not self.object_list.query.where: | |
# This query that avoids a count(*) alltogether is | |
# stolen from https://djangosnippets.org/snippets/2593/ | |
cursor = connections[self.object_list.db].cursor() | |
cursor.execute("SELECT reltuples FROM pg_class WHERE relname = %s", | |
[self.object_list.query.model._meta.db_table]) | |
self._count = int(cursor.fetchone()[0]) | |
else : | |
self._count = self.object_list.count() | |
cache.set(key, self._count, 5 * 60) | |
except : | |
# AttributeError if object_list has no count() method. | |
# TypeError if object_list.count() requires arguments | |
# (i.e. is of type list). | |
self._count = len(self.object_list) | |
return self._count | |
count = property(_get_count) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment