Pull to refresh

Comments 55

django-pagination чем не подходит?
{% autopaginate something 10 %} перед блоком for и {% paginate %} после этого блока чтоб вывести блок с ссылками на другие страницы
такие вещи надо делать в коде, а не в шаблоне
>Практически в любом сайте есть нужда разбить вывод информации на страницы
>вывод информации
>вывод
То, что относится к выводу информации, логично делать в шаблоне.
  1. 1) пагинатор обрабатывает входящие данные — это не логика вывода
  2. 2) часто его поведение нужно менять — например добавить первым элементом прикрепленный объект (или последнюю запись в блоге, или горячую новость)
задача контроллера — отдать данные,
а задача вьюхи — их отобразить в нужном виде — с постраничным разбиением или нет
контроллер меняет состояние модели, а вьюха — отображает. в жанге немного другая модель поведения, так что не надо притягивать теорию из другого места.
не знаю, где вы читали. «Generally, a view retrieves data according to the parameters, loads a template and renders the template with the retrieved data.» — в доке джанги
я не только читал, а использую её почти каждый день.
Что за бред?
Другая модель — это какая? В джанго — классическая MVC, правда элементы по-другому называются. Так что bsn абсолютно прав.
А вам стоит почитать что такое MVC и не городить чепуху.
С таким подходом вообще нельзя пользоваться тэгами/фильтрами джанги.
pluralize, floatformat, striptags, truncatewords, etc — все делать в коде, а в шаблоне только выводить готовые значения.
они не работают с пользовательским вводом. в отличии от пагинации.
Серьезно? А с каким таким вводом они работают, и что пользовательского в паджинации?
?page=1&per_page=10 — это блять называется пользовательский ввод
Ответье всё-таки на вопрос.
В запросе с аргументами ?page=2&truncate_words=20 что именно является пользовательским вводом, непременно требующим обработки во вью, и на основе чего вы делаете такие суждения?
тогда скажи мне, чья задача отдать 404 ошибку? по твоей логике — вьюшки)
> такие вещи надо делать в коде, а не в шаблоне

Заставлять верстальщика дергать разработчика, чтобы поменять количество элементов на странице — можно, но не обязательно.

Подписываюсь под тем, что сказали тут выше на тему «пагинация — логика вывода, и хорошо если она в шаблоне».
Такие вещи надо делать там, где удобнее, а не изобретать велосипед.
Не могу ничего по существу сказать, перечислю только достоинства simplepagination
1. Возможность использовать несколько видов пагинации в одном проекте (например список блогов выводится с пагинацией в стиле хабра, а список комментариев выводится в стиле digg)
2. Сохраняются все GET параметры (если у тебя урл выглядит example.com/?page=1&vasya=petya то ссылка на вторую страницу сохранит &vasya=petya)
3. Много всяких настроек, например разрешается менять количество отображаемых записей через GET параметры, по дефолту такие параметры принимаются в DEBUG моде.

Наверное ещё какие то есть вещи, я просто django-pagination толком не смотрел
Это примерно так и работает, только вместо {% autopaginate something 10 %}, в шаблон попадает уже обрезаное количество something. А сколько показывать может быть указано в 4ёх местах.
1. settings.py проекта
2. переменная внутри класса бэкенда
3. параметер к декоратору
4. GET параметр

вместо {% paginate %} пишется
{% include paginator.template %}

В принципе в этом плане здесь то же самое.
Кстати вот то что получилось из того кода который я в посте дал, примерно пол часа ушло на всё про всё picbite.com/image/85193plfdy/ На сколько я смог протестировать это точная копия работы хабра пагинатора.
Из документации по джанго

A view function, or view for short, is simply a Python function that takes a Web request and returns a Web response.

view возвращающий словарь — ломает соглашения. Это минус.

Я тоже за то, что включение пагинатора и определение его параметров должно происходить в шаблоне. Это логика отображения, а не логика организации данных. В джанго: Модель — Model, контроллер — View, а вью — Template. То есть реализация MVC немного не такая, как, например в RoR. Вот ссылка на раздел фака, где об этом написано docs.djangoproject.com/en/dev/faq/general/#django-appears-to-be-a-mvc-framework-but-you-call-the-controller-the-view-and-the-view-the-template-how-come-you-don-t-use-the-standard-names
Хороший django app должен следовать соглашениям.
тут view — результат работы цепочки декораторов, именно HttpResponse она и вернет.
Стандартный джанговский паджинатор реализует разбиение на страницы во вьюхе, а не в шаблоне. Это насчет соглашений.
Не буду вступать в ваш спор про MVC, но насчёт HttpRespnse хочу уточнить, что декоратор пагинации стоит под декоратором превращающий этот словарь в HttpResponse (render_to). Я декоратором render_to пользуюсь в любом случае, есть ли там пагинация или нет, с render_to_response нужно просто ещё всё время передавать ContextRequest(request) или что то типа этого, а это запаривает.
Вместо render_to_response можно использовать direct_to_template, он почти то же самое делает, но передает RequestContext. Мне, по крайней мере, это значительно ближе идеи с декоратором. Но вопрос синтаксиса и привычки, конечно.
UFO just landed and posted this here
Да, я тоже раньше пользовался render_to от Piranha
Понравилась возможность подключения бэкендов.

Мои хотелки:

1. Паджинация без декораторов, миддлварей, процессоров контекста. Фильтрация — во вьюхе. django-simplepagination вроде довольно несложно переделывается в версию, которая не требует render_to, за что ей плюс.

2. Дигг-разбиение на страницы со страницами как по возростанию, так и по убыванию.

Страницы по убыванию (текущая имеет самый большой номер) нужны там, где публикуется со временем какое-то содержание, и все новое попадает в начало. Это позволяет ссылаться на какую-то страницу без риска, что на ней поменяются элементы.

Страницы по возрастанию (текущая имеет самый маленький номер) нужны там, где все новое попадает в конец. Например, различные топы и рейтинги. Чтоб было можно ссылаться на топ рейтинга, на 2ю страницу топа и т.д.
Я как создатель аппы, не могу представить как первый пункт можно воплотить в жизнь. В принципе из всё что вы перечислили нужен только декоратор. Если у вас python 2.3 и нет поддержки декораторов то всё равно код либы не будет работать, там по моему пару вещей используются которые возможны только в 2.4 и выше.

Второй пункт более интересный, но опять же можно написать свой бэкенд, назвать его reversedigg и задать там любую логику, не обязательно хакать существующий.
1. Как я себе это представлял — делаем функцию, которая принимает QuerySet (+ вероятно, request) и возвращает подготовленный объект Paginator. Декоратор, на первый взгляд, зависит только от GET-параметров запроса и QuerySet'а с объектами. Ну и request можно не передавать, если руками передавать текущую страницу (+ возможно, нужное кол-во элементов на странице и тд, что там еще). В качестве бонуса отвязываемся от обязанности держать номера страниц в GET-параметрах (правда, возможно, еще где-то отвязывать нужно будет).

2. Конечно без хаканья, мне поэтому возможность подключения бэкендов и понравилась.
тьфу, не Paginator, а словарь data. Но это без разницы, один черт добавить результат в контекст и все.
Я попробую объяснить в двух словах как работает декоратор (как работает бэкенд я примерно объяснил в посте)

1. К нему попадет словарь который выходит из вьюшки, например это {'object_list': posts, 'something': 'anything'}

По дефолту ищется 'object_list', если передать в декоратор key=«another» то будет искать «another» и пытаться его делить на страницы, что в нашем случае выкинет эксепшн.
После этого создаётся стандартный джанговский объект Paginator, из которого достаются две вещи
а. Количество страниц
б. Все объекты которые должны быть на странице

Пункт «б» теперь подменяет наш object_list, в результате в шаблоне когда вы делаете {% for i in object_list %} вы итерируете уже по обрубленому списку.

На этом этапе все нужные данные о пагинации собраны и отдаются в бэкенд который возвращает словарь, его мы и вставляем в словарь который был во вьюшке. В резульате у нас вьюшка превращается в {'object_list': posts[only_pages:from_current_page], 'paginator': <dictionary_from_backend>}
Ну вот, все отлично, я вроде так все и понял.

делаем функцию:

    simple_paginate(object_list, request):
        # берет qs (или имя модели, тогда _default_manager.all()) и request, 
        # возвращает paginator: <dict from backend>.update('object_list' : from_current_page)

        # можно сделать как обертку к декоратору, или декоратор сделать как обертку к этой ф-и.


во вьюхе

   def my_view(request):
       how_to_name_it= simple_paginate(MyModel.objects.all(), request)
       return direct_to_template('my_template.html', {'how_to_name_it': how_to_name_it})


или полный эквивалент:

   def my_view(request):
       how_to_name_it= simple_paginate(MyModel.objects.all(), request)
       return direct_to_template('my_template.html', 
                 {'how_to_name_it': how_to_name_it, 'object_list': how_to_name_it['object_list']})

Можно еще, как и в стандартном паджинаторе, не от паджинатора плясать, а от страницы. Тогда наличие object_list у нее логичнее. Короче это я так, по-быстрому накидал, думаю, можно что-нибудь такое выдумать. Если заниматься этим не очень хочется, могу заняться.
Ну в теории да, это всё конечно можно сделать и через функции, вопрос нафига? Практически весь код декоратора придётся дублировать.
Чтоб можно было использовать без декоратора render_to.

А дублировать ничего не придется. Можно или написать обертку для декоратора (подготовить данные, задекорировать внутреннюю функцию, вызвать ее, распарсить ответ), или сделать декоратор оберткой над функцией. Кода и там, и там совсем немного получается вроде.

Как-то так:

def simple_paginate(queryset, request, *args, **kwargs)
    @paginate(*args, **kwargs)
    def inner(request)
        return {'object_list': queryset}

    data = inner(request)
    # в data будет лежать
    # {'object_list': <отфильтрованный object_list>, 'paginator': <наш паджинатор>}

    # вместо этого можно возвращать 2 значения. 
    # Или принимать словарь с контекстом и обновлять его.
    data['paginator']['object_list'] = data['object_list']

    return data['paginator'] 

def my_view(request)
   paginator = simple_paginate(Post.objects.all(), request, style='digg')
   return direct_to_template('my_template.html', {'paginator': paginator})
UFO just landed and posted this here
@render_to('blog_posts.html')
@paginate(style='digg', per_page=15)

Ну тогда уж ждём

@get(object_to_get, form_to_render)
@post(object_to_save)

Некрасивый синтаксис.
Такое уже есть. Но, имхо, это размазывает настройки. Логичнее всё-таки в urls.py :)
Имхо, стандартного паджинатора вполне достаточно. А тут классы какие-то городить… Зачем?
По-моему, must-have штука — паджинация в стиле digg:

1 2 3… 13 14 15 >16< 17 18 19… 453 454 455,
+ то же самое, но в обратную сторону

а одним блоком располагать 455 страниц — не здорово.

Ну и заметьте, то, что может дать стандартный паджинатор, «в классах» используется, там дописано то, чего стандартный паджинатор не умеет.

А в django-simplepagination, как показалось, вполне хороший код (в отличие от многих других приложений для паджинации) + вполне удобное дописывание своих методов разбиения на страницы (тоже в отличие от других), что еще нужно.
Посмотрел код, и увидел обёртку над стандартным паджинатором, завязанную на render_to. Ничего такого супер-уникального я не нашёл. Ну да, прикольно иметь кучу бэкендов для паджинации, но это как бы всё, что я вижу интересного. А минус в виде render_to мне не нравится.
P.S. Всякие digg-style делаются довольно просто и без дополнительных app. А habra-style пишется за пару минут без всяких бэкендов.
Мне тоже не нравится минус в виде render_to, но он убирается 7 строчками кода, которые я привел выше.

А от джанго-приложения не нужно ничего супер-уникального, лучше когда хорошо и просто. Если оно позволит сэкономить в следующий раз некоторое количество времени — уже здорово.

Это ж хорошо, когда куча небольших приложений, каждое делает что-то свое небольшое. Зато не нужно над этим чем-то небольшим каждый раз думать, писать код и тесты.

Ну а digg-style все-таки не очень просто делается. Ну мне так кажется, по крайней мере. Я вот сходу не напишу сейчас тут в комментарии алгоритм дигг-паджинации, когда первая страница имеет наибольший номер. Например, в этом алгоритме хорошо бы предусмотреть, чтоб элемент, попав на страницу, оставался на нем навсегда. Т.е. решить, что делать: или на главной (первой) число элементов, получается, колебаться будет от 0 до per_page (тогда перманентность номера будет сохранена, но главная страница будет выглядеть тупо), или от per_page до per_page*2 (перманентность начинает сохраняться с вице-главной страницы, главная выглядит нормально). Если просто выводить per_page элементов на последней странице, то элементы на других будут скакать туда-сюда и все плюсы от обратной нумерации улетучиваются. Каждый раз над такими штуками думать — это ж с ума сайти можно. Или гляньте на шаблон для обычной digg-style, 1.6 Kb кода. Короче, очень за вас рад, если для вас все это довольно просто, все с ходу предусмотрели и быстрее самому написать, чем в готовом разобраться.
> По-моему, must-have штука — паджинация в стиле digg:

Делаеться очень просто стандартными средствами.
Вот вариант digg пагинации стандартным способом, при этом у многих он не работает, довольно таки много кода и опять же подключать к конкретным вьюшкам не так просто, нужно менять urls.py, задавать настройки которые нельзя изменить на разных страницах, не сохраняются GET параметры и ещё много минусов. blog.localkinegrinds.com/2007/09/06/digg-style-pagination-in-django/

Не знаю в чём проблема у людей юзать render_to декоратор, по моему его и так около 80% рунетовских джангистов используют, byteflow его сильно популязировал.

Вообще попробуйте один раз подключить в своём сайте simplepagination и вы поймёте в чём его фишка… он чертовски прост.
Ну это неверная реализация просто. У меня есть гораздо более лаконичная, где не надо ничего в urls.py пихать и т.п. Подключается простым {% load pagination %}, ссылаясь на стандартную паджинацию.

Про render_to я ничего не говорил, сам его использую.
Может быть, не буду спорить, но пока в реале не видел лучше реализаций.
Судя по тому, какие мотивационные примеры были приведены, тут вообще нужен только generic-вью (и никаких кастомных), который паджинирует из-коробки :) В общем, не совсем ясно, зачем это. Если смысл был сделать digg-style паджинацию, то это делает стандартный паджинатор + темплейттег для простоты. Такое решение позволяет и в генериках использовать его. Ваш паджинатор к генерикам прикручивается?
Генерики просто обрезают QS, они не помогают нарисовать сам пагинатор. В моём случае обрезает декоратор (обрезает причём встроенным в джанго Paginator, наверное им же пользуется и генерики), делает ещё пару нужных вещей такие как проверка что такая страница вообще существует, манипулирует данными из GET запроса, после этого передаёт все нужные данные в бекенд который занимается только тем как помочь НАРИСОВАТЬ пагинацию. Вот для этого и нужно это приложение, что бы можно было ещё и нарисовать пагинацию кроме того как обрезать qs.
Что значит «генерики обрезаеют QS»? Генерики позволяют сделать пагинацию простейшим способом. А вот манипулировать GET-запросами штуке, которая отвечает за пагинацию я бы ни в коем случае не доверил. «Не ломать GET-запрос» и «манипулировать» им это разные совсем вещи.

В общем, неясен совсем профит от именно simplepagination. То что вы назваете pagination_backend это есть просто template-tag, зачем вводить лишнюю сущность?
В теории да, обычно это реализуют через inclution tag, но он ведь не является у меня темплейт тэгом, поэтому зачем называть это template-tag если он таковым не является?
Имхо, логичнее именно template-tag. Это же часто повторяющееся действие на уровне представления, поэтому это делать надо средствами представления (я имею в виду вывод пагинаторских контролов, а не пагинацию как таковую). А средства представления, позволяющие автоматизировать дейтвия, это как раз язык шаблонов, а именно tamplate tag. Как-то так, имхо.
Обрезают QS имелось ввиду что если вы передали ему Example.objects.all() то из этого queryset на выходе будут например Example.objects.all()[:10]

Так это не обрезание QS, это просто limit выборки. Любой нормальный пагинатор так делать должен.
Sign up to leave a comment.

Articles