From b08b584cf7f90680cd725ea48e49195176f4c2eb Mon Sep 17 00:00:00 2001 From: Nitya Oberoi Date: Fri, 24 Jun 2011 15:58:53 -0400 Subject: [PATCH 01/11] Allow for styling of ellipsis via span tag --- pagination/templates/pagination/pagination.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pagination/templates/pagination/pagination.html b/pagination/templates/pagination/pagination.html index fe566a8..ab53309 100644 --- a/pagination/templates/pagination/pagination.html +++ b/pagination/templates/pagination/pagination.html @@ -14,7 +14,7 @@ {{ page }} {% endifequal %} {% else %} - ... + ... {% endif %} {% endfor %} {% if page_obj.has_next %} From b78fc3ac8c8a4dbd053420314d6bf347d49be2ad Mon Sep 17 00:00:00 2001 From: Ben Plesser Date: Thu, 29 Sep 2011 14:57:44 -0400 Subject: [PATCH 02/11] Added a new Infinite Pagination tag that removes MySQL count. It's a bit custom, so we may want to move the logic to the Yipit project --- pagination/paginator.py | 61 ++++++++++++++++++- .../pagination/yipit_pagination.html | 22 +++++++ pagination/templatetags/pagination_tags.py | 53 +++++++++++++++- 3 files changed, 132 insertions(+), 4 deletions(-) create mode 100644 pagination/templates/pagination/yipit_pagination.html diff --git a/pagination/paginator.py b/pagination/paginator.py index f67aa23..e1fb499 100644 --- a/pagination/paginator.py +++ b/pagination/paginator.py @@ -46,12 +46,13 @@ def page(self, number): pass else: raise EmptyPage('That page contains no results') - return InfinitePage(page_items, number, self) + return YipitInfinitePage(page_items, number, self) def _get_count(self): """ Returns the total number of objects, across all pages. """ + raise NotImplementedError count = property(_get_count) @@ -59,6 +60,7 @@ def _get_num_pages(self): """ Returns the total number of pages. """ + raise NotImplementedError num_pages = property(_get_num_pages) @@ -67,6 +69,7 @@ def _get_page_range(self): Returns a 1-based range of pages for iterating through within a template for loop. """ + raise NotImplementedError page_range = property(_get_page_range) @@ -80,6 +83,7 @@ def has_next(self): """ Checks for one more item than last on this page. """ + try: next_item = self.paginator.object_list[ self.number * self.paginator.per_page] @@ -107,6 +111,61 @@ def previous_link(self): return self.paginator.link_template % (self.number - 1) return None + +class YipitInfinitePaginator(InfinitePaginator): + + def page(self, number): + """ + Returns a Page object for the given 1-based page number. Subclasses InfinitePaginator + to remove MySQL count query for paginated lists + """ + number = self.validate_number(number) + bottom = (number - 1) * self.per_page + top = bottom + self.per_page + page_items = self.object_list[bottom:top] + # check moved from validate_number + if not page_items: + if number == 1 and self.allow_empty_first_page: + pass + else: + raise EmptyPage('That page contains no results') + return YipitInfinitePage(page_items, number, self) + + def _get_count(self): + """ + Returns the total number of objects, across all pages. Hack here returns 100k + items in order to avoid costly Count MySQL query + """ + return 100000 + count = property(_get_count) + + def _get_num_pages(self): + """ + Returns the total number of pages. Hardcoded for same reason as _get_count() + """ + + return 1000 + num_pages = property(_get_num_pages) + + def _get_page_range(self): + """ + Returns a 1-based range of pages for iterating through within + a template for loop. Hardcoded for same reason as _get_count() + """ + + return range(1000) + page_range = property(_get_page_range) + + +class YipitInfinitePage(InfinitePage): + + def has_next(self): + """ + Checks for one more item than last on this page. + """ + return False + + class FinitePaginator(InfinitePaginator): """ Paginator for cases when the list of items is already finite. diff --git a/pagination/templates/pagination/yipit_pagination.html b/pagination/templates/pagination/yipit_pagination.html new file mode 100644 index 0000000..df72ba2 --- /dev/null +++ b/pagination/templates/pagination/yipit_pagination.html @@ -0,0 +1,22 @@ +{% if is_paginated %} +{% load i18n %} + +{% endif %} diff --git a/pagination/templatetags/pagination_tags.py b/pagination/templatetags/pagination_tags.py index ae843b1..b799a98 100644 --- a/pagination/templatetags/pagination_tags.py +++ b/pagination/templatetags/pagination_tags.py @@ -7,6 +7,7 @@ from django.http import Http404 from django.core.paginator import Paginator, InvalidPage from django.conf import settings +from pagination.paginator import YipitInfinitePaginator register = template.Library() @@ -52,6 +53,46 @@ def do_autopaginate(parser, token): raise template.TemplateSyntaxError('%r tag takes one required ' + 'argument and one optional argument' % split[0]) + +def no_count_paginate(parser, token): + """ + Removes objecting counting MySQL queries by subclassing the django-pagination, InfinitePaginator. + May be wiser to move this custom tag over to Yipit project. + """ + split = token.split_contents() + as_index = None + context_var = None + for i, bit in enumerate(split): + if bit == 'as': + as_index = i + break + if as_index is not None: + try: + context_var = split[as_index + 1] + except IndexError: + raise template.TemplateSyntaxError("Context variable assignment " + + "must take the form of {%% %r object.example_set.all ... as " + + "context_var_name %%}" % split[0]) + del split[as_index:as_index + 2] + if len(split) == 2: + return AutoPaginateNode(split[1], paginator=YipitInfinitePaginator) + elif len(split) == 3: + return AutoPaginateNode(split[1], paginate_by=split[2], + context_var=context_var, paginator=YipitInfinitePaginator) + elif len(split) == 4: + try: + orphans = int(split[3]) + except ValueError: + raise template.TemplateSyntaxError(u'Got %s, but expected integer.' + % split[3]) + return AutoPaginateNode(split[1], paginate_by=split[2], orphans=orphans, + context_var=context_var, paginator=YipitInfinitePaginator) + else: + raise template.TemplateSyntaxError('%r tag takes one required ' + + 'argument and one optional argument' % split[0]) + + + class AutoPaginateNode(template.Node): """ Emits the required objects to allow for Digg-style pagination. @@ -68,9 +109,13 @@ class AutoPaginateNode(template.Node): It is recommended to use *{% paginate %}* after using the autopaginate tag. If you choose not to use *{% paginate %}*, make sure to display the list of available pages, or else the application may seem to be buggy. + + The paginator attribute has been added for Yipit customization to allow for + a custom paginator (YipitInfinitePaginator). The default is the django core Paginator + object, which is what AutoPaginateNode used pre-forking. """ def __init__(self, queryset_var, paginate_by=DEFAULT_PAGINATION, - orphans=DEFAULT_ORPHANS, context_var=None): + orphans=DEFAULT_ORPHANS, context_var=None, paginator=Paginator): self.queryset_var = template.Variable(queryset_var) if isinstance(paginate_by, int): self.paginate_by = paginate_by @@ -78,6 +123,7 @@ def __init__(self, queryset_var, paginate_by=DEFAULT_PAGINATION, self.paginate_by = template.Variable(paginate_by) self.orphans = orphans self.context_var = context_var + self.paginator = paginator def render(self, context): key = self.queryset_var.var @@ -86,7 +132,7 @@ def render(self, context): paginate_by = self.paginate_by else: paginate_by = self.paginate_by.resolve(context) - paginator = Paginator(value, paginate_by, self.orphans) + paginator = self.paginator(value, paginate_by) try: page_obj = paginator.page(context['request'].page) except InvalidPage: @@ -225,6 +271,7 @@ def paginate(context, window=DEFAULT_WINDOW, hashtag=''): except KeyError, AttributeError: return {} -register.inclusion_tag('pagination/pagination.html', takes_context=True)( +register.inclusion_tag('pagination/yipit_pagination.html', takes_context=True)( paginate) register.tag('autopaginate', do_autopaginate) +register.tag('countfree_paginate', no_count_paginate) From 19f52a4e6f260a198215d8bbac54cbf948acb4f7 Mon Sep 17 00:00:00 2001 From: Ben Plesser Date: Mon, 19 Dec 2011 14:15:53 -0500 Subject: [PATCH 03/11] Added queryset count caching to the core autopaginate template tag logic --- pagination/paginator.py | 64 ++------ pagination/templatetags/pagination_tags.py | 177 ++++++++++++--------- 2 files changed, 118 insertions(+), 123 deletions(-) diff --git a/pagination/paginator.py b/pagination/paginator.py index e1fb499..c37e2f3 100644 --- a/pagination/paginator.py +++ b/pagination/paginator.py @@ -112,59 +112,25 @@ def previous_link(self): return None -class YipitInfinitePaginator(InfinitePaginator): - - def page(self, number): - """ - Returns a Page object for the given 1-based page number. Subclasses InfinitePaginator - to remove MySQL count query for paginated lists - """ - number = self.validate_number(number) - bottom = (number - 1) * self.per_page - top = bottom + self.per_page - page_items = self.object_list[bottom:top] - # check moved from validate_number - if not page_items: - if number == 1 and self.allow_empty_first_page: - pass - else: - raise EmptyPage('That page contains no results') - return YipitInfinitePage(page_items, number, self) +class CachedCountPaginator(Paginator): + """ + Nearly Identical to the core Django Paginator with the exception that it uses model cache counts. + Saves big MySQL Count queries. Object_list must be a model queryset. + """ def _get_count(self): - """ - Returns the total number of objects, across all pages. Hack here returns 100k - items in order to avoid costly Count MySQL query - """ - return 100000 + "Returns the total number of objects, across all pages." + if self._count is None: + try: + self._count = self.object_list.get_cached_count() + 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) - def _get_num_pages(self): - """ - Returns the total number of pages. Hardcoded for same reason as _get_count() - """ - - return 1000 - num_pages = property(_get_num_pages) - - def _get_page_range(self): - """ - Returns a 1-based range of pages for iterating through within - a template for loop. Hardcoded for same reason as _get_count() - """ - - return range(1000) - page_range = property(_get_page_range) - - -class YipitInfinitePage(InfinitePage): - - def has_next(self): - """ - Checks for one more item than last on this page. - """ - return False - class FinitePaginator(InfinitePaginator): """ diff --git a/pagination/templatetags/pagination_tags.py b/pagination/templatetags/pagination_tags.py index b799a98..3bd6276 100644 --- a/pagination/templatetags/pagination_tags.py +++ b/pagination/templatetags/pagination_tags.py @@ -7,7 +7,7 @@ from django.http import Http404 from django.core.paginator import Paginator, InvalidPage from django.conf import settings -from pagination.paginator import YipitInfinitePaginator +from pagination.paginator import CachedCountPaginator register = template.Library() @@ -17,80 +17,56 @@ INVALID_PAGE_RAISES_404 = getattr(settings, 'PAGINATION_INVALID_PAGE_RAISES_404', False) -def do_autopaginate(parser, token): - """ - Splits the arguments to the autopaginate tag and formats them correctly. - """ - split = token.split_contents() - as_index = None - context_var = None - for i, bit in enumerate(split): - if bit == 'as': - as_index = i - break - if as_index is not None: - try: - context_var = split[as_index + 1] - except IndexError: - raise template.TemplateSyntaxError("Context variable assignment " + - "must take the form of {%% %r object.example_set.all ... as " + - "context_var_name %%}" % split[0]) - del split[as_index:as_index + 2] - if len(split) == 2: - return AutoPaginateNode(split[1]) - elif len(split) == 3: - return AutoPaginateNode(split[1], paginate_by=split[2], - context_var=context_var) - elif len(split) == 4: - try: - orphans = int(split[3]) - except ValueError: - raise template.TemplateSyntaxError(u'Got %s, but expected integer.' - % split[3]) - return AutoPaginateNode(split[1], paginate_by=split[2], orphans=orphans, - context_var=context_var) - else: - raise template.TemplateSyntaxError('%r tag takes one required ' + - 'argument and one optional argument' % split[0]) +# def do_autopaginate(parser, token, PaginateNode=None): +# """ +# Splits the arguments to the autopaginate tag and formats them correctly. +# """ +# PaginateNode = AutoPaginateNode if not PaginateNode else PaginateNode +# split = token.split_contents() +# as_index = None +# context_var = None +# cache_counts = None +# for i, bit in enumerate(split): +# if bit == 'as': +# as_index = i +# continue +# if bit == 'cache_counts': +# cache_counts = i +# continue +# if as_index is not None: +# try: +# context_var = split[as_index + 1] +# except IndexError: +# raise template.TemplateSyntaxError("Context variable assignment " + +# "must take the form of {%% %r object.example_set.all ... as " + +# "context_var_name %%}" % split[0]) +# del split[as_index:as_index + 2] +# if cache_counts is not None: +# del split[cache_counts:cache_counts + 1] +# PaginateNode = CachedAutoPaginateNode +# if len(split) == 2: +# return PaginateNode(split[1]) +# elif len(split) == 3: +# return PaginateNode(split[1], paginate_by=split[2], +# context_var=context_var) +# elif len(split) == 4: +# try: +# orphans = int(split[3]) +# except ValueError: +# raise template.TemplateSyntaxError(u'Got %s, but expected integer.' +# % split[3]) +# return PaginateNode(split[1], paginate_by=split[2], orphans=orphans, +# context_var=context_var) +# else: +# raise template.TemplateSyntaxError('%r tag takes one required ' + +# 'argument and one optional argument' % split[0]) -def no_count_paginate(parser, token): - """ - Removes objecting counting MySQL queries by subclassing the django-pagination, InfinitePaginator. - May be wiser to move this custom tag over to Yipit project. - """ - split = token.split_contents() - as_index = None - context_var = None - for i, bit in enumerate(split): - if bit == 'as': - as_index = i - break - if as_index is not None: - try: - context_var = split[as_index + 1] - except IndexError: - raise template.TemplateSyntaxError("Context variable assignment " + - "must take the form of {%% %r object.example_set.all ... as " + - "context_var_name %%}" % split[0]) - del split[as_index:as_index + 2] - if len(split) == 2: - return AutoPaginateNode(split[1], paginator=YipitInfinitePaginator) - elif len(split) == 3: - return AutoPaginateNode(split[1], paginate_by=split[2], - context_var=context_var, paginator=YipitInfinitePaginator) - elif len(split) == 4: - try: - orphans = int(split[3]) - except ValueError: - raise template.TemplateSyntaxError(u'Got %s, but expected integer.' - % split[3]) - return AutoPaginateNode(split[1], paginate_by=split[2], orphans=orphans, - context_var=context_var, paginator=YipitInfinitePaginator) - else: - raise template.TemplateSyntaxError('%r tag takes one required ' + - 'argument and one optional argument' % split[0]) - +# def cached_count_paginate(parser, token): +# """ +# Caches Queryset counts to improve the performance of the built-in Django Paginator +# """ +# return do_autopaginate(parser, token, PaginateNode=CachedAutoPaginateNode) class AutoPaginateNode(template.Node): @@ -151,6 +127,59 @@ def render(self, context): return u'' +class CachedAutoPaginateNode(AutoPaginateNode): + + def __init__(self, queryset_var, paginate_by=DEFAULT_PAGINATION, + orphans=DEFAULT_ORPHANS, context_var=None, paginator=CachedCountPaginator): + + super(CachedAutoPaginateNode, self).__init__(queryset_var, paginate_by=paginate_by, + orphans=orphans, context_var=context_var, paginator=paginator) + + +def do_autopaginate(parser, token, PaginateNode=AutoPaginateNode): + """ + Splits the arguments to the autopaginate tag and formats them correctly. + """ + split = token.split_contents() + as_index = None + context_var = None + cache_counts = None + for i, bit in enumerate(split): + if bit == 'as': + as_index = i + continue + if bit == 'cache_counts': + cache_counts = i + continue + if as_index is not None: + try: + context_var = split[as_index + 1] + except IndexError: + raise template.TemplateSyntaxError("Context variable assignment " + + "must take the form of {%% %r object.example_set.all ... as " + + "context_var_name %%}" % split[0]) + del split[as_index:as_index + 2] + if cache_counts is not None: + del split[cache_counts:cache_counts + 1] + PaginateNode = CachedAutoPaginateNode + if len(split) == 2: + return PaginateNode(split[1]) + elif len(split) == 3: + return PaginateNode(split[1], paginate_by=split[2], + context_var=context_var) + elif len(split) == 4: + try: + orphans = int(split[3]) + except ValueError: + raise template.TemplateSyntaxError(u'Got %s, but expected integer.' + % split[3]) + return PaginateNode(split[1], paginate_by=split[2], orphans=orphans, + context_var=context_var) + else: + raise template.TemplateSyntaxError('%r tag takes one required ' + + 'argument and one optional argument' % split[0]) + + def paginate(context, window=DEFAULT_WINDOW, hashtag=''): """ Renders the ``pagination/pagination.html`` template, resulting in a @@ -274,4 +303,4 @@ def paginate(context, window=DEFAULT_WINDOW, hashtag=''): register.inclusion_tag('pagination/yipit_pagination.html', takes_context=True)( paginate) register.tag('autopaginate', do_autopaginate) -register.tag('countfree_paginate', no_count_paginate) +# register.tag('cachedpaginate', cached_count_paginate) From a657f8edca9f4187b4e83666dca0e0c5e886a887 Mon Sep 17 00:00:00 2001 From: Mingwei Gu Date: Tue, 20 Dec 2011 15:12:42 -0500 Subject: [PATCH 04/11] seo pagination --- pagination/paginator.py | 2 +- .../templates/pagination/seo_pagination.html | 16 +++ pagination/templatetags/pagination_tags.py | 98 +++++++++++++++++++ 3 files changed, 115 insertions(+), 1 deletion(-) create mode 100644 pagination/templates/pagination/seo_pagination.html diff --git a/pagination/paginator.py b/pagination/paginator.py index c37e2f3..45ad75a 100644 --- a/pagination/paginator.py +++ b/pagination/paginator.py @@ -114,7 +114,7 @@ def previous_link(self): class CachedCountPaginator(Paginator): """ - Nearly Identical to the core Django Paginator with the exception that it uses model cache counts. + Nearly Identical to the core Django Paginator with the exception that it uses queryset cache counts. Saves big MySQL Count queries. Object_list must be a model queryset. """ diff --git a/pagination/templates/pagination/seo_pagination.html b/pagination/templates/pagination/seo_pagination.html new file mode 100644 index 0000000..9b4fb30 --- /dev/null +++ b/pagination/templates/pagination/seo_pagination.html @@ -0,0 +1,16 @@ +{% if is_paginated %} +{% load i18n %} + +{% endif %} diff --git a/pagination/templatetags/pagination_tags.py b/pagination/templatetags/pagination_tags.py index 3bd6276..37f120b 100644 --- a/pagination/templatetags/pagination_tags.py +++ b/pagination/templatetags/pagination_tags.py @@ -300,7 +300,105 @@ def paginate(context, window=DEFAULT_WINDOW, hashtag=''): except KeyError, AttributeError: return {} + +def seo_paginate(context, window=DEFAULT_WINDOW, hashtag=''): + try: + paginator = context['paginator'] + page_obj = context['page_obj'] + page_range = paginator.page_range + # Calculate the record range in the current page for display. + records = {'first': 1 + (page_obj.number - 1) * paginator.per_page} + records['last'] = records['first'] + paginator.per_page - 1 + if records['last'] + paginator.orphans >= paginator.count: + records['last'] = paginator.count + + middle = page_obj.number + length = len(page_range) + + # If the current page is too close to the beginning or end, let us + # pretend that the current page is at least 'window' length away + if middle < window: + middle = window + if middle > length - window: + middle = length - window + + # Now we look around our current page, making sure that we don't wrap + # around. + current_start = middle-1-window + + #Increment current_start + current_start += 1 + + if current_start < 0: + current_start = 0 + current_end = middle-1+window + if current_end < 0: + current_end = 0 + + pages = [] + + if (current_end > length - 2): + current_end = length + + current = set(page_range[current_start:current_end]) + first = set(page_range[0:2]) + + # If there's no overlap between the first set of pages and the current + # set of pages, then there's a possible need for elusion. + if len(first.intersection(current)) == 0: + first_list = list(first) + first_list.sort() + second_list = list(current) + second_list.sort() + pages.extend(first_list) + diff = second_list[0] - first_list[-1] + # If there is a gap of two, between the last page of the first + # set and the first page of the current set, then we're missing a + # page. + if diff == 2: + pages.append(second_list[0] - 1) + # If the difference is just one, then there's nothing to be done, + # as the pages need no elusion and are correct. + elif diff == 1: + pass + # Otherwise, there's a bigger gap which needs to be signaled for + # elusion, by pushing a None value to the page list. + else: + pages.append(None) + pages.extend(second_list) + else: + unioned = list(first.union(current)) + unioned.sort() + pages.extend(unioned) + + if (current_end < length): + pages.append(None) + + to_return = { + 'MEDIA_URL': settings.MEDIA_URL, + 'pages': pages, + 'records': records, + 'page_obj': page_obj, + 'paginator': paginator, + 'hashtag': hashtag, + 'is_paginated': paginator.count > paginator.per_page, + } + if 'request' in context: + getvars = context['request'].GET.copy() + if 'page' in getvars: + del getvars['page'] + if len(getvars.keys()) > 0: + to_return['getvars'] = "&%s" % getvars.urlencode() + else: + to_return['getvars'] = '' + return to_return + except KeyError, AttributeError: + return {} + + register.inclusion_tag('pagination/yipit_pagination.html', takes_context=True)( paginate) +register.inclusion_tag('pagination/seo_pagination.html', takes_context=True)( + seo_paginate) register.tag('autopaginate', do_autopaginate) # register.tag('cachedpaginate', cached_count_paginate) From 9a393bd9039e38907caf32a92b431c100ed6852e Mon Sep 17 00:00:00 2001 From: Mingwei Gu Date: Tue, 20 Dec 2011 16:55:00 -0500 Subject: [PATCH 05/11] Rename SEO Paginate to Tailless Paginate --- .../{seo_pagination.html => tailless_pagination.html} | 0 pagination/templatetags/pagination_tags.py | 6 +++--- 2 files changed, 3 insertions(+), 3 deletions(-) rename pagination/templates/pagination/{seo_pagination.html => tailless_pagination.html} (100%) diff --git a/pagination/templates/pagination/seo_pagination.html b/pagination/templates/pagination/tailless_pagination.html similarity index 100% rename from pagination/templates/pagination/seo_pagination.html rename to pagination/templates/pagination/tailless_pagination.html diff --git a/pagination/templatetags/pagination_tags.py b/pagination/templatetags/pagination_tags.py index 37f120b..b2d14e8 100644 --- a/pagination/templatetags/pagination_tags.py +++ b/pagination/templatetags/pagination_tags.py @@ -301,7 +301,7 @@ def paginate(context, window=DEFAULT_WINDOW, hashtag=''): return {} -def seo_paginate(context, window=DEFAULT_WINDOW, hashtag=''): +def tailless_paginate(context, window=DEFAULT_WINDOW, hashtag=''): try: paginator = context['paginator'] page_obj = context['page_obj'] @@ -398,7 +398,7 @@ def seo_paginate(context, window=DEFAULT_WINDOW, hashtag=''): register.inclusion_tag('pagination/yipit_pagination.html', takes_context=True)( paginate) -register.inclusion_tag('pagination/seo_pagination.html', takes_context=True)( - seo_paginate) +register.inclusion_tag('pagination/tailless_pagination.html', takes_context=True)( + tailless_paginate) register.tag('autopaginate', do_autopaginate) # register.tag('cachedpaginate', cached_count_paginate) From 287e66847c2d0df92a05ce75ba931d525afaa169 Mon Sep 17 00:00:00 2001 From: Mingwei Gu Date: Wed, 21 Dec 2011 16:51:52 -0500 Subject: [PATCH 06/11] Removed reference to YipitInfinitePage --- pagination/paginator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pagination/paginator.py b/pagination/paginator.py index 45ad75a..65b7c10 100644 --- a/pagination/paginator.py +++ b/pagination/paginator.py @@ -46,7 +46,7 @@ def page(self, number): pass else: raise EmptyPage('That page contains no results') - return YipitInfinitePage(page_items, number, self) + return InfinitePage(page_items, number, self) def _get_count(self): """ From bf34cdc569a05bc65a7d47a6b79fe3d59387950b Mon Sep 17 00:00:00 2001 From: Suneel Chakravorty Date: Thu, 8 Mar 2012 18:03:16 -0500 Subject: [PATCH 07/11] added FakeInfinitePaginator which bypasses COUNT in django admin, making loading deal admin page much faster --- pagination/paginator.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/pagination/paginator.py b/pagination/paginator.py index 65b7c10..4345cb3 100644 --- a/pagination/paginator.py +++ b/pagination/paginator.py @@ -73,7 +73,32 @@ def _get_page_range(self): raise NotImplementedError page_range = property(_get_page_range) +class FakeInfinitePaginator(InfinitePaginator): + """ + Paginator designed for cases when it's not important to know how many total + pages. This is useful for any object_list that has no count() method or can + be used to improve performance for MySQL by removing counts. + The orphans parameter has been removed for simplicity and there's a link + template string for creating the links to the next and previous pages. + """ + + def _get_count(self): + """ + Returns the total number of objects, across all pages. + """ + + return 10000 + count = property(_get_count) + + def _get_num_pages(self): + """ + Returns the total number of pages. + """ + + return 100 + num_pages = property(_get_num_pages) + class InfinitePage(Page): def __repr__(self): From 60ea6a64ab76ac11ecd4cfb4ba067110595e2e4f Mon Sep 17 00:00:00 2001 From: Steve Pulec Date: Thu, 8 Mar 2012 18:47:22 -0500 Subject: [PATCH 08/11] Bumping to 1.0.8 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 751d914..305b0a1 100644 --- a/setup.py +++ b/setup.py @@ -1,6 +1,6 @@ from setuptools import setup, find_packages -version = '1.0.7' +version = '1.0.8' LONG_DESCRIPTION = """ How to use django-pagination From c4ebf59a7aa788d2379e13e7674765adc492b796 Mon Sep 17 00:00:00 2001 From: Steve Pulec Date: Wed, 31 Jul 2013 16:22:38 -0400 Subject: [PATCH 09/11] Renaming distribution to be Yipit specific --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 305b0a1..7afc57d 100644 --- a/setup.py +++ b/setup.py @@ -102,7 +102,7 @@ """ setup( - name='django-pagination', + name='django-pagination-yipit', version=version, description="django-pagination", long_description=LONG_DESCRIPTION, From 6ff7bcaf93c92e14d36b2227c08edb7d5ccf3e51 Mon Sep 17 00:00:00 2001 From: Steve Pulec Date: Wed, 31 Jul 2013 17:29:56 -0400 Subject: [PATCH 10/11] add manifest --- MANIFEST | 17 +++++++++++++++++ setup.py | 28 ++++++++++++++-------------- 2 files changed, 31 insertions(+), 14 deletions(-) create mode 100644 MANIFEST diff --git a/MANIFEST b/MANIFEST new file mode 100644 index 0000000..f8ad971 --- /dev/null +++ b/MANIFEST @@ -0,0 +1,17 @@ +# file GENERATED by distutils, do NOT edit +CONTRIBUTORS.txt +LICENSE.txt +setup.py +docs/index.txt +docs/install.txt +docs/usage.txt +pagination/__init__.py +pagination/middleware.py +pagination/models.py +pagination/paginator.py +pagination/tests.py +pagination/templates/pagination/pagination.html +pagination/templates/pagination/tailless_pagination.html +pagination/templates/pagination/yipit_pagination.html +pagination/templatetags/__init__.py +pagination/templatetags/pagination_tags.py diff --git a/setup.py b/setup.py index 7afc57d..170f3f3 100644 --- a/setup.py +++ b/setup.py @@ -1,6 +1,6 @@ from setuptools import setup, find_packages -version = '1.0.8' +version = '1.0.9' LONG_DESCRIPTION = """ How to use django-pagination @@ -9,12 +9,12 @@ ``django-pagination`` allows for easy Digg-style pagination without modifying your views. -There are really 5 steps to setting it up with your projects (not including +There are really 5 steps to setting it up with your projects (not including installation, which is covered in INSTALL.txt in this same directory.) 1. List this application in the ``INSTALLED_APPS`` portion of your settings file. Your settings file might look something like:: - + INSTALLED_APPS = ( # ... 'pagination', @@ -23,7 +23,7 @@ 2. Install the pagination middleware. Your settings file might look something like:: - + MIDDLEWARE_CLASSES = ( # ... 'pagination.middleware.PaginationMiddleware', @@ -33,7 +33,7 @@ Note that context processors are set by default implicitly, so to set them explicitly, you need to copy and paste this code into your under the value TEMPLATE_CONTEXT_PROCESSORS:: - + ("django.core.context_processors.auth", "django.core.context_processors.debug", "django.core.context_processors.i18n", @@ -46,27 +46,27 @@ 5. Decide on a variable that you would like to paginate, and use the - autopaginate tag on that variable before iterating over it. This could + autopaginate tag on that variable before iterating over it. This could take one of two forms (using the canonical ``object_list`` as an example variable): - + {% autopaginate object_list %} - + This assumes that you would like to have the default 20 results per page. If you would like to specify your own amount of results per page, you can specify that like so: - + {% autopaginate object_list 10 %} - + Note that this replaces ``object_list`` with the list for the current page, so you can iterate over the ``object_list`` like you normally would. - + 6. Now you want to display the current page and the available pages, so somewhere after having used autopaginate, use the paginate inclusion tag: - + {% paginate %} - + This does not take any arguments, but does assume that you have already called autopaginate, so make sure to do so first. @@ -92,7 +92,7 @@ ``PAGINATION_DEFAULT_ORPHANS`` The number of orphans allowed. According to the Django documentation, orphans are defined as:: - + The minimum number of items allowed on the last page, defaults to zero. ``PAGINATION_INVALID_PAGE_RAISES_404`` From bd4573f34cfff8bfcead251c179028695a9cbd0f Mon Sep 17 00:00:00 2001 From: Steve Pulec Date: Wed, 31 Jul 2013 18:06:58 -0400 Subject: [PATCH 11/11] Fix setup.py for templates --- MANIFEST | 17 ----------------- setup.py | 1 + 2 files changed, 1 insertion(+), 17 deletions(-) delete mode 100644 MANIFEST diff --git a/MANIFEST b/MANIFEST deleted file mode 100644 index f8ad971..0000000 --- a/MANIFEST +++ /dev/null @@ -1,17 +0,0 @@ -# file GENERATED by distutils, do NOT edit -CONTRIBUTORS.txt -LICENSE.txt -setup.py -docs/index.txt -docs/install.txt -docs/usage.txt -pagination/__init__.py -pagination/middleware.py -pagination/models.py -pagination/paginator.py -pagination/tests.py -pagination/templates/pagination/pagination.html -pagination/templates/pagination/tailless_pagination.html -pagination/templates/pagination/yipit_pagination.html -pagination/templatetags/__init__.py -pagination/templatetags/pagination_tags.py diff --git a/setup.py b/setup.py index 170f3f3..60b9f88 100644 --- a/setup.py +++ b/setup.py @@ -118,6 +118,7 @@ url='http://django-pagination.googlecode.com/', license='BSD', packages=find_packages(), + package_data={'pagination': ['templates/pagination/*.html']}, include_package_data=True, zip_safe=False, )