Opened 9 years ago

Closed 4 years ago

#25513 closed New feature (fixed)

Refactor the admin paginator customizations to make them reuseable

Reported by: Vlada Macek Owned by: Nick Pope
Component: Core (Other) Version: dev
Severity: Normal Keywords: paginator, ellipsis
Cc: Triage Stage: Ready for checkin
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

For large page counts I miss a standard way to display only interesting blocks of page links in the paginator. I guess Django should include one.

What do you think about integrating my implementation to django.core.paginator.Page drafted below?
Template example at the bottom.

from django.core.paginator import Paginator, Page

class EllipsisPaginator(Paginator):
    def __init__(self, *args, **kwargs):
        # number of page links to always display after the first page
        self.start_wing = kwargs.pop('start_wing', 1)
        # number of page links to always display around the current page
        self.island_wings = kwargs.pop('island_wings', 2)
        # number of page links to always display before the last page
        self.end_wing = kwargs.pop('end_wing', 1)

        super(EllipsisPaginator, self).__init__(*args, **kwargs)

    def _get_page(self, *args, **kwargs):
        return EllipsisPage(*args, **kwargs)


class EllipsisPage(Page):
    def pages_with_ellipsis(self):
        """
        Generates the list of page numbers for large page counts.
        Yields '...' where the range of links should be omitted.

        >>> pp = EllipsisPaginator(object_list=range(38), per_page=3)
        >>> list(pp.page(1).pages_with_ellipsis())
        [1, 2, 3, '...', 12, 13]
        >>> list(pp.page(2).pages_with_ellipsis())
        [1, 2, 3, 4, '...', 12, 13]
        >>> list(pp.page(3).pages_with_ellipsis())
        [1, 2, 3, 4, 5, '...', 12, 13]
        >>> list(pp.page(4).pages_with_ellipsis())
        [1, 2, 3, 4, 5, 6, '...', 12, 13]
        >>> list(pp.page(5).pages_with_ellipsis())
        [1, 2, 3, 4, 5, 6, 7, '...', 12, 13]
        >>> list(pp.page(6).pages_with_ellipsis())
        [1, 2, 3, 4, 5, 6, 7, 8, '...', 12, 13]
        >>> list(pp.page(7).pages_with_ellipsis())
        [1, 2, '...', 5, 6, 7, 8, 9, '...', 12, 13]
        >>> list(pp.page(8).pages_with_ellipsis())
        [1, 2, '...', 6, 7, 8, 9, 10, 11, 12, 13]
        >>> list(pp.page(9).pages_with_ellipsis())
        [1, 2, '...', 7, 8, 9, 10, 11, 12, 13]
        >>> list(pp.page(10).pages_with_ellipsis())
        [1, 2, '...', 8, 9, 10, 11, 12, 13]
        >>> list(pp.page(11).pages_with_ellipsis())
        [1, 2, '...', 9, 10, 11, 12, 13]
        >>> list(pp.page(12).pages_with_ellipsis())
        [1, 2, '...', 10, 11, 12, 13]
        >>> list(pp.page(13).pages_with_ellipsis())
        [1, 2, '...', 11, 12, 13]
        """
        num = 1

        end_of_start_wing = min(self.paginator.num_pages, self.paginator.start_wing+1)

        for num in xrange(1, end_of_start_wing+1):
            yield num

        island_start = self.number - self.paginator.island_wings

        if num < island_start-2:
            yield '...'
            num = island_start
        else:
            num += 1

        island_end = min(self.paginator.num_pages, self.number + self.paginator.island_wings)

        for num in xrange(num, island_end+1):
            yield num

        start_of_end_wing = self.paginator.num_pages - self.paginator.end_wing

        if num < start_of_end_wing-2:
            yield '...'
            num = start_of_end_wing
        else:
            num += 1

        for num in xrange(num, self.paginator.num_pages+1):
            yield num

Usage in the template:

<ul class="pagination">
    <li rel="prev">
        <a {% if page.has_previous %}href="?page={{ page.previous_page_number }}"{% endif %}>
            Previous
        </a>
    </li>

    {% for pg in page.pages_with_ellipsis %}

	{% if pg != '...' %}
	    <li {% if pg == page.number %}class="active"{% endif %}>
		<a {% if pg != page.number %}href="?page={{ pg }}"{% endif %}>
		    {{ pg }}
		</a>
	    </li>
	{% else %}
	    <li><span>&hellip;</span></li>
	{% endif %}

    {% endfor %}

    <li rel="next" {% if page.has_next %}class="highlight"{% endif %}>
	<a {% if page.has_next %}href="?page={{ page.next_page_number }}"{% endif %}>
	    Next
	</a>
    </li>
</ul>

Attachments (1)

shot-20151006-1543.png (2.9 KB ) - added by Vlada Macek 9 years ago.
A screenshot of the paginator.

Download all attachments as: .zip

Change History (14)

by Vlada Macek, 9 years ago

Attachment: shot-20151006-1543.png added

A screenshot of the paginator.

comment:1 by Vlada Macek, 9 years ago

Attached is the screenshot of how it can look.

The method is a simple and effetcive Python generator jumping over iteresting blocks of pages.

comment:2 by Tim Graham, 9 years ago

The admin contains a similar paginator in the form of a template tag. Maybe it would be worth trying to refactoring that into django.core.paginator so it's more easily reuseable. I'm not sure how well the code will generalize.

comment:3 by Tim Graham, 9 years ago

Summary: Paginator support for large page countsRefactor the admin paginator customizations to make them reuseable
Triage Stage: UnreviewedAccepted
Version: 1.9a1master

comment:4 by Sasha Gaevsky, 9 years ago

Owner: changed from nobody to Sasha Gaevsky
Status: newassigned

comment:5 by Sasha Gaevsky, 9 years ago

Has patch: set
Last edited 9 years ago by Tim Graham (previous) (diff)

comment:6 by Tim Graham, 9 years ago

Needs documentation: set

Looks like it's on the right track, but the new class should be documented too.

comment:7 by Tim Graham, 6 years ago

Needs documentation: unset

comment:8 by Tim Graham, 6 years ago

Patch needs improvement: set

comment:9 by Nick Pope, 4 years ago

Keywords: ellipsis added
Owner: changed from Sasha Gaevsky to Nick Pope
Patch needs improvement: unset

Updated with a new PR.

comment:10 by Carlton Gibson, 4 years ago

Triage Stage: AcceptedReady for checkin

comment:11 by Carlton Gibson <carlton@…>, 4 years ago

In b203ec70:

Refs #25513 -- Adjusted admin pagination to be 1-indexed.

comment:12 by Carlton Gibson <carlton@…>, 4 years ago

In f35840c1:

Refs #25513 -- Fixed admin pagination elision bounds.

It doesn't make sense to elide a single page number which could be a
clickable link to that page. We only want to elide two or more pages.

comment:13 by Carlton Gibson <carlton@…>, 4 years ago

Resolution: fixed
Status: assignedclosed

In 0a306f7:

Fixed #25513 -- Extracted admin pagination to Paginator.get_elided_page_range().

Note: See TracTickets for help on using tickets.
Back to Top