This is just a modified version of a previous snippet to make it work with unicode and with class-based ListView paginator_class
To use it put this in your urls.py:
from youapp.fileyouchose import NamePaginator
urlpatterns = patterns('',
url(r'^example/(?P<page>[0-9]+)/$', ListView.as_view(model=myModel,template_name="mytemplate.html",paginator_class=NamePaginator,paginate_by=25), name="url_name"),
And then in your template something like this would work:
{% if page_obj.has_other_pages %}
<div class="row">
<div class="span12">
<div class="pagination">
<ul>
{% if page_obj.has_previous %}
<li><a href="{% url page page=page_obj.previous_page_number %}">Prev</a></li>
{% else %}
<li class="disabled"><a>Prev</a></li>
{% endif %}
{% for p in page_obj.paginator.pages %}
<li {% if p == page_obj %}class="active"{% endif %}>
<a href="{% url category_page page=p.number %}">{{ p }}</a>
</li>
{% endfor %}
{% if page_obj.has_next %}
<li><a href="{% url page page=page_obj.next_page_number %}">Next</a></li>
{% else %}
<li class="disabled"><a>Next</a></li>
{% endif %}
</ul>
</div>
</div>
</div>
{% endif %}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 | import string
from django.core.paginator import InvalidPage, EmptyPage
class NamePaginator(object):
"""Pagination for string-based objects"""
def __init__(self, queryset, paginate_by=25, orphans=0, allow_empty_first_page=True):
# We ignore allow_empty_first_page and orphans, just here for compliance
self.pages = []
self.object_list = queryset
self.count = len(self.object_list)
# chunk up the objects so we don't need to iterate over the whole list for each letter
chunks = {}
# we sort them by the first model ordering key
for obj in self.object_list:
if queryset: obj_str = unicode(getattr(obj, obj._meta.ordering[0]))
else: obj_str = unicode(obj)
# some of my models had "first_name" "last_name" sorting, and some first_names
# were empty so if it fails you can try sorting by the second ordering key
# which worked for me but do your own thing
try:
letter = unicode.upper(obj_str[0])
except:
obj_str = unicode(getattr(obj, obj._meta.ordering[1]))
letter = unicode.upper(obj_str[1])
if letter not in chunks: chunks[letter] = []
chunks[letter].append(obj)
# the process for assigning objects to each page
current_page = NamePage(self)
for letter in string.ascii_uppercase:
if letter not in chunks:
current_page.add([], letter)
continue
sub_list = chunks[letter] # the items in object_list starting with this letter
new_page_count = len(sub_list) + current_page.count
# first, check to see if sub_list will fit or it needs to go onto a new page.
# if assigning this list will cause the page to overflow...
# and an underflow is closer to per_page than an overflow...
# and the page isn't empty (which means len(sub_list) > per_page)...
if new_page_count > paginate_by and \
abs(paginate_by - current_page.count) < abs(paginate_by - new_page_count) and \
current_page.count > 0:
# make a new page
self.pages.append(current_page)
current_page = NamePage(self)
current_page.add(sub_list, letter)
# if we finished the for loop with a page that isn't empty, add it
if current_page.count > 0: self.pages.append(current_page)
def page(self, num):
"""Returns a Page object for the given 1-based page number."""
if len(self.pages) == 0:
return None
elif num > 0 and num <= len(self.pages):
return self.pages[num-1]
else:
raise InvalidPage
@property
def num_pages(self):
"""Returns the total number of pages"""
return len(self.pages)
class NamePage(object):
def __init__(self, paginator):
self.paginator = paginator
self.object_list = []
self.letters = []
@property
def count(self):
return len(self.object_list)
@property
def start_letter(self):
if len(self.letters) > 0:
self.letters.sort(key=str.upper)
return self.letters[0]
else: return None
@property
def end_letter(self):
if len(self.letters) > 0:
self.letters.sort(key=str.upper)
return self.letters[-1]
else: return None
@property
def number(self):
return self.paginator.pages.index(self) + 1
# just added the methods I needed to use in the templates
# feel free to add the ones you need too
def has_other_pages(self):
return len(self.object_list) > 0
def has_previous(self):
return self.paginator.pages.index(self)
def has_next(self):
return self.paginator.pages.index(self) + 2
def next_page_number(self):
return self.paginator.pages.index(self) + 2
def previous_page_number(self):
return self.paginator.pages.index(self)
def add(self, new_list, letter=None):
if len(new_list) > 0: self.object_list = self.object_list + new_list
if letter: self.letters.append(letter)
def __repr__(self):
if self.start_letter == self.end_letter:
return self.start_letter
else:
return '%c-%c' % (self.start_letter, self.end_letter)
|
More like this
- Template tag - list punctuation for a list of items by shapiromatron 11 months, 2 weeks ago
- JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 11 months, 3 weeks ago
- Serializer factory with Django Rest Framework by julio 1 year, 6 months ago
- Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 7 months ago
- Help text hyperlinks by sa2812 1 year, 8 months ago
Comments
Thanks for this, made a few modifications which might be useful:
#
Please login first before commenting.