Декоратор для удобного постраничного разбиения

Стандартное решение предлагаемое джанго для постраничного разбиения списка объектов - это класс django.core.paginator.ObjectPaginator

Здесь можно посмотреть примеры использования ObjectPaginator [djangoproject.com]

Однако, использование ObjectPaginator в голом виде не совсем удобно. В самом деле, нужно:
- извлечь номер страницы из GET-данных или URL запроса
- создать QuerySet объектов для пагинации
- создать ObjectPaginator, передав ему этот QuerySet
- получить объекты текущей страницы, путём вызова метода get_page у пагинатора

Вся эта рутина так и просится в декоратор. Без лишних слов, собственно, код декоратора )


def paged(paged_list_name, per_page=10):
def decorator(func):
def wrapper(request, *args, **kwargs):
result = func(request, *args, **kwargs)
if not isinstance(result, dict):
return result
try:
page = int(request.GET.get('page', 1))
except ValueError:
page = 1
from django.core.paginator import ObjectPaginator
paginator = ObjectPaginator(result['paged_qs'], per_page)
result[paged_list_name] = paginator.get_page(page - 1)
result['page'] = page
result['pages'] = paginator.pages
return result
return wrapper
return decorator

def paged(paged_list_name, per_page=10):
def decorator(func):
def wrapper(request, *args, **kwargs):
result = func(request, *args, **kwargs)
if not isinstance(result, dict):
return result
try:
page = int(request.GET.get(‘page’, 1))
except ValueError:
page = 1
from django.core.paginator import ObjectPaginator
paginator = ObjectPaginator(result[‘paged_qs’], per_page)
result[paged_list_name] = paginator.get_page(page - 1)
result[‘page’] = page
result[‘pages’] = paginator.pages
return result
return wrapper
return decorator

Поясню, как я его использую. Дело в том, что все мои view возвращают не объект класса HttpResponse, но обыкновенный словарь, который потом обрабатывается декоратором render_to. Подробнее об этом вы можете прочитать в этой статье [web-brains.com]

Ну а в декораторе paged я просто дополняю этот словарь, который вернула моя view. Я кладу в словарь элементы с ключами page, pages и список объектов. Имя элемента со списком задаётся в первом позиционном аргументе декоратора. Чтобы декоратор работал, нужно из view вернуть QuerySet объектов для пагинации в элементе с ключом paged_qs.

Код ещё сырой, я его написал час назад. До этого я использова не такой навороченный декоратор. Если у вас есть замечания и предложения, с радостью выслушаю.

Ну, и, конечно, было бы неплохо, чтобы список со страничками сам строился ;-)
В этом нам поможет следующий inclusion tag.


@register.inclusion_tag('site/pagination.html',takes_context=True)
def pagination(context,adjacent_pages=5):
page_list = range(
max(1,context['page'] - adjacent_pages),
min(context['pages'],context['page'] + adjacent_pages) + 1)
lower_page = None
higher_page = None

if not 1 == context['page']:
lower_page = context['page'] - 1

if not 1 in page_list:
page_list.insert(0,1)
if not 2 in page_list:
page_list.insert(1,'.')

if not context['pages'] == context['page']:
higher_page = context['page'] + 1

if not context['pages'] in page_list:
if not context['pages'] - 1 in page_list:
page_list.append('.')
page_list.append(context['pages'])
get_params = '&'.join(['%s=%s' % (x[0],','.join(x[1])) for x in
context['request'].GET.iteritems() if not x[0] == 'page'])
if get_params:
get_params = get_params + '&'

return {
'get_params': get_params,
'lower_page': lower_page,
'higher_page': higher_page,
'page': context['page'],
'pages': context['pages'],
'page_list': page_list}

@register.inclusion_tag(‘site/pagination.html’,takes_context=True)
def pagination(context,adjacent_pages=5):
page_list = range(
max(1,context[‘page’] - adjacent_pages),
min(context[‘pages’],context[‘page’] + adjacent_pages) + 1)
lower_page = None
higher_page = None

if not 1 == context[‘page’]:
lower_page = context[‘page’] - 1

if not 1 in page_list:
page_list.insert(0,1)
if not 2 in page_list:
page_list.insert(1,’.’)

if not context[‘pages’] == context[‘page’]:
higher_page = context[‘page’] + 1

if not context[‘pages’] in page_list:
if not context[‘pages’] - 1 in page_list:
page_list.append(‘.’)
page_list.append(context[‘pages’])
get_params = ‘&’.join([‘%s=%s’ % (x[0],’,’.join(x[1])) for x in
context[‘request’].GET.iteritems() if not x[0] == ‘page’])
if get_params:
get_params = get_params + ‘&’

return {
‘get_params’: get_params,
‘lower_page’: lower_page,
‘higher_page’: higher_page,
‘page’: context[‘page’],
‘pages’: context[‘pages’],
‘page_list’: page_list}


А это pagination.html


<div class="pagination">
{% ifnotequal pages 1 %}
Страницы:
{% if lower_page %}
<a href="?page={{ lower_page }}">&laquo;</a>
{% endif %}
{% for number in page_list %}
{% ifequal number "." %}...
{% else %}
{% ifequal number page %}{{ number }}{% endifequal %}
{% ifnotequal number page %}
<a href="?page={{ number }}">{{ number }}</a>
{% endifnotequal %}
{% endifequal %}
{% endfor %}
{% if higher_page %}
<a href="?page={{ higher_page }}">&raquo;</a>
{% endif %}
{% endifnotequal %}
</div>

<div class=”pagination”>
{% ifnotequal pages 1 %}
Страницы:
{% if lower_page %}
<a href=”?page={{ lower_page }}”>&laquo;</a>
{% endif %}
{% for number in page_list %}
{% ifequal number “.” %}…
{% else %}
{% ifequal number page %}{{ number }}{% endifequal %}
{% ifnotequal number page %}
<a href=”?page={{ number }}”>{{ number }}</a>
{% endifnotequal %}
{% endifequal %}
{% endfor %}
{% if higher_page %}
<a href=”?page={{ higher_page }}”>&raquo;</a>
{% endif %}
{% endifnotequal %}
</div>
Add post to:   Google Slashdot Yahoo Digg Technorati Delicious Bobrdobr.ru Newsland.ru Smi2.ru Rumarkz.ru Vaau.ru Memori.ru Rucity.com Moemesto.ru News2.ru Mister-Wong.ru Yandex.ru Myscoop.ru 100zakladok.ru
Make comment

Comments

No comments for this post
comment submission form

Required. 30 chars of fewer.

Required.

captcha image Please, enter symbols, which you see on the image