Skip to content

Instantly share code, notes, and snippets.

@nffdiogosilva
Created March 31, 2016 22:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nffdiogosilva/584238ae97ea4ba1b893a6de22272251 to your computer and use it in GitHub Desktop.
Save nffdiogosilva/584238ae97ea4ba1b893a6de22272251 to your computer and use it in GitHub Desktop.
A paginator class
# -*- coding:utf-8 -*-
from __future__ import unicode_literals
class Paginator(object):
"""
Class that defines a Paginator.
"""
def __init__(self, current_page, total_pages, boundaries=1, around=0, fail_silently=False):
self.fail_silently = fail_silently
# Initialize private attributes
self._total_pages = None
self._current_page = None
self._boundaries = None
self._around = None
self.total_pages = total_pages
self.current_page = current_page
self.boundaries = boundaries
self.around = around
self._pagination_list = []
self.pagination_list = []
@property
def current_page(self):
return self._current_page
@property
def total_pages(self):
return self._total_pages
@property
def around(self):
return self._around
@property
def boundaries(self):
return self._boundaries
@current_page.setter
def current_page(self, value):
if self.fail_silently:
# Takes into account when current_page value is smaller than 1 and bigger than total_pages value
value = max(1, min(self.total_pages, value))
else:
if value < 1:
raise ValueError("Current page value can't be smaller than 1")
if value > self.total_pages:
raise ValueError("Current page value can't be bigger than total_pages value")
self._current_page = value
@total_pages.setter
def total_pages(self, value):
if self.fail_silently:
# Takes into account when total_pages is smaller than 1
value = max(1, value)
else:
if value < 1:
raise ValueError("Total pages value can't be smaller than 1")
self._total_pages = value
@around.setter
def around(self, value):
if self.fail_silently:
# Takes into account when around is smaller than 0 and bigger than total_pages
value = max(0, min(self.total_pages, value))
else:
if value < 0:
raise ValueError("Around value can't be smaller than 0")
if value > self.total_pages:
raise ValueError("Around value can't be bigger than total_pages value")
self._around = value
@boundaries.setter
def boundaries(self, value):
if self.fail_silently:
# Takes into account when boundaries is smaller than 0 and when is bigger than total_pages value
value = max(1, min(self.total_pages, value))
else:
if value < 1:
raise ValueError("Boundaries value can't be smaller than 1")
if value > self.total_pages:
raise ValueError("Boundaries value can't be bigger than total_pages value")
self._boundaries = value
def _add_ellipsis_to_pagination_list(self):
"""
Initialize a pagination list with ellipses for omitted values.
:return: The final pagination list, with ellipses, if necessary.
"""
final_pagination_list = []
for index, number in enumerate(self._pagination_list):
final_pagination_list.append(str(number))
if index + 1 < len(self._pagination_list):
if self._pagination_list[index] + 1 != self._pagination_list[index + 1]:
final_pagination_list.append('...')
return final_pagination_list
def _clean_arounds(self, arounds_list):
"""
Removes any number that is already included in boundaries lists (0 included).
:param arounds_list: A list with arounds values in it.
:return: A cleaned arounds list, with no boundaries or zero values in it.
"""
for boundary_list in self.get_boundaries():
arounds_list = [around for around in arounds_list if around not in boundary_list]
if 0 in arounds_list:
arounds_list.remove(0)
return arounds_list
def _clean_boundaries(self, boundaries_list):
"""
Removes current page from boundary list, if found it.
:param boundaries_list: A list with boundaries values in it.
:return: A cleaned boundaries list, without the current page value.
"""
if self.current_page in boundaries_list:
boundaries_list.remove(self.current_page)
return boundaries_list
def get_arounds(self):
"""
Calculates left and right arounds.
:return: A tuple with two lists: a list for each left and right arounds.
"""
current_page_min_range = max(1, self.current_page - 1)
current_page_max_range = min(self.current_page + 1, self.total_pages)
max_range = max(1, self.current_page - self.around - 1)
min_range = min(self.total_pages, self.current_page + self.around + 1)
left_arounds = sorted(range(current_page_min_range, max_range, -1), reverse=False)
right_arounds = range(current_page_max_range, min_range)
return self._clean_arounds(left_arounds), self._clean_arounds(right_arounds)
def get_boundaries(self):
"""
Calculates left and right boundaries.
:return: A tuple with two lists: a list for each left and right boundaries.
"""
left_boundaries = range(1, self.boundaries + 1)
left_boundaries = [boundary for boundary in left_boundaries if boundary < self.current_page] or [1]
right_boundaries = sorted(range(self.total_pages, self.total_pages - self.boundaries, -1), reverse=False)
right_boundaries = [boundary for boundary in right_boundaries if boundary > self.current_page] or [self.total_pages]
return self._clean_boundaries(left_boundaries), self._clean_boundaries(right_boundaries)
def set_pagination_list(self):
"""
Initializes a pagination list given the current object arguments.
:return: A pagination list
"""
self._pagination_list = self.get_boundaries()[0] + self.get_arounds()[0] + [self.current_page] + \
self.get_arounds()[1] + self.get_boundaries()[1]
self.pagination_list = self._add_ellipsis_to_pagination_list()
return self.pagination_list
def next_page(self):
"""
Sets the next page as the current page
:return: The Paginator instance (updated)
"""
if self.current_page + 1 <= self.total_pages:
self.current_page += 1
else:
if self.fail_silently:
self.current_page = self.total_pages
else:
raise ValueError("Current page value can't be bigger than total pages value")
self.pagination_list = self.set_pagination_list()
return self
def previous_page(self):
"""
Sets the previous page as the current page
:return: The Paginator instance (updated)
"""
if self.current_page - 1 >= 1:
self.current_page -= 1
else:
if self.fail_silently:
self.current_page = 1
else:
raise ValueError("Current page value can't be smaller than 1")
self.pagination_list = self.set_pagination_list()
return self
def __str__(self):
return ' '.join(self.set_pagination_list())
def __repr__(self):
return 'Paginator: {0}'.format(self.__str__())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment