Fixed #18355 -- Added ordering options to list based generic views.

Added MultipleObjectMixin.ordering and get_ordering().

Refs #21450.
This commit is contained in:
Peter Harley 2014-05-19 22:27:09 +01:00 committed by Tim Graham
parent 0ad4672c0f
commit 2724cdbff6
9 changed files with 126 additions and 7 deletions

View File

@ -343,7 +343,14 @@ class BaseDateListView(MultipleObjectMixin, DateMixin, View):
""" """
raise NotImplementedError('A DateView must provide an implementation of get_dated_items()') raise NotImplementedError('A DateView must provide an implementation of get_dated_items()')
def get_dated_queryset(self, ordering=None, **lookup): def get_ordering(self):
"""
Returns the field or fields to use for ordering the queryset; uses the
date field by default.
"""
return '-%s' % self.get_date_field() if self.ordering is None else self.ordering
def get_dated_queryset(self, **lookup):
""" """
Get a queryset properly filtered according to `allow_future` and any Get a queryset properly filtered according to `allow_future` and any
extra lookup kwargs. extra lookup kwargs.
@ -354,9 +361,6 @@ class BaseDateListView(MultipleObjectMixin, DateMixin, View):
allow_empty = self.get_allow_empty() allow_empty = self.get_allow_empty()
paginate_by = self.get_paginate_by(qs) paginate_by = self.get_paginate_by(qs)
if ordering is not None:
qs = qs.order_by(ordering)
if not allow_future: if not allow_future:
now = timezone.now() if self.uses_datetime_field else timezone_today() now = timezone.now() if self.uses_datetime_field else timezone_today()
qs = qs.filter(**{'%s__lte' % date_field: now}) qs = qs.filter(**{'%s__lte' % date_field: now})
@ -412,7 +416,7 @@ class BaseArchiveIndexView(BaseDateListView):
""" """
Return (date_list, items, extra_context) for this request. Return (date_list, items, extra_context) for this request.
""" """
qs = self.get_dated_queryset(ordering='-%s' % self.get_date_field()) qs = self.get_dated_queryset()
date_list = self.get_date_list(qs, ordering='DESC') date_list = self.get_date_list(qs, ordering='DESC')
if not date_list: if not date_list:
@ -451,7 +455,7 @@ class BaseYearArchiveView(YearMixin, BaseDateListView):
'%s__lt' % date_field: until, '%s__lt' % date_field: until,
} }
qs = self.get_dated_queryset(ordering='-%s' % date_field, **lookup_kwargs) qs = self.get_dated_queryset(**lookup_kwargs)
date_list = self.get_date_list(qs) date_list = self.get_date_list(qs)
if not self.get_make_object_list(): if not self.get_make_object_list():

View File

@ -4,6 +4,7 @@ from django.core.paginator import Paginator, InvalidPage
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from django.db.models.query import QuerySet from django.db.models.query import QuerySet
from django.http import Http404 from django.http import Http404
from django.utils import six
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.views.generic.base import TemplateResponseMixin, ContextMixin, View from django.views.generic.base import TemplateResponseMixin, ContextMixin, View
@ -20,6 +21,7 @@ class MultipleObjectMixin(ContextMixin):
context_object_name = None context_object_name = None
paginator_class = Paginator paginator_class = Paginator
page_kwarg = 'page' page_kwarg = 'page'
ordering = None
def get_queryset(self): def get_queryset(self):
""" """
@ -42,8 +44,20 @@ class MultipleObjectMixin(ContextMixin):
'cls': self.__class__.__name__ 'cls': self.__class__.__name__
} }
) )
ordering = self.get_ordering()
if ordering:
if isinstance(ordering, six.string_types):
ordering = (ordering,)
queryset = queryset.order_by(*ordering)
return queryset return queryset
def get_ordering(self):
"""
Return the field or fields to use for ordering the queryset.
"""
return self.ordering
def paginate_queryset(self, queryset, page_size): def paginate_queryset(self, queryset, page_size):
""" """
Paginate the queryset, if needed. Paginate the queryset, if needed.

View File

@ -118,6 +118,7 @@ ListView
* :attr:`~django.views.generic.list.MultipleObjectMixin.context_object_name` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_context_object_name`] * :attr:`~django.views.generic.list.MultipleObjectMixin.context_object_name` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_context_object_name`]
* :attr:`~django.views.generic.base.View.http_method_names` * :attr:`~django.views.generic.base.View.http_method_names`
* :attr:`~django.views.generic.list.MultipleObjectMixin.model` * :attr:`~django.views.generic.list.MultipleObjectMixin.model`
* :attr:`~django.views.generic.list.MultipleObjectMixin.ordering` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_ordering`]
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginate_by` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_paginate_by`] * :attr:`~django.views.generic.list.MultipleObjectMixin.paginate_by` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_paginate_by`]
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginate_orphans` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_paginate_orphans`] * :attr:`~django.views.generic.list.MultipleObjectMixin.paginate_orphans` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_paginate_orphans`]
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginator_class` * :attr:`~django.views.generic.list.MultipleObjectMixin.paginator_class`
@ -301,6 +302,7 @@ ArchiveIndexView
* :attr:`~django.views.generic.dates.DateMixin.date_field` [:meth:`~django.views.generic.dates.DateMixin.get_date_field`] * :attr:`~django.views.generic.dates.DateMixin.date_field` [:meth:`~django.views.generic.dates.DateMixin.get_date_field`]
* :attr:`~django.views.generic.base.View.http_method_names` * :attr:`~django.views.generic.base.View.http_method_names`
* :attr:`~django.views.generic.list.MultipleObjectMixin.model` * :attr:`~django.views.generic.list.MultipleObjectMixin.model`
* :attr:`~django.views.generic.list.MultipleObjectMixin.ordering` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_ordering`]
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginate_by` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_paginate_by`] * :attr:`~django.views.generic.list.MultipleObjectMixin.paginate_by` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_paginate_by`]
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginate_orphans` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_paginate_orphans`] * :attr:`~django.views.generic.list.MultipleObjectMixin.paginate_orphans` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_paginate_orphans`]
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginator_class` * :attr:`~django.views.generic.list.MultipleObjectMixin.paginator_class`
@ -338,6 +340,7 @@ YearArchiveView
* :attr:`~django.views.generic.base.View.http_method_names` * :attr:`~django.views.generic.base.View.http_method_names`
* :attr:`~django.views.generic.dates.YearArchiveView.make_object_list` [:meth:`~django.views.generic.dates.YearArchiveView.get_make_object_list`] * :attr:`~django.views.generic.dates.YearArchiveView.make_object_list` [:meth:`~django.views.generic.dates.YearArchiveView.get_make_object_list`]
* :attr:`~django.views.generic.list.MultipleObjectMixin.model` * :attr:`~django.views.generic.list.MultipleObjectMixin.model`
* :attr:`~django.views.generic.list.MultipleObjectMixin.ordering` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_ordering`]
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginate_by` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_paginate_by`] * :attr:`~django.views.generic.list.MultipleObjectMixin.paginate_by` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_paginate_by`]
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginate_orphans` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_paginate_orphans`] * :attr:`~django.views.generic.list.MultipleObjectMixin.paginate_orphans` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_paginate_orphans`]
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginator_class` * :attr:`~django.views.generic.list.MultipleObjectMixin.paginator_class`
@ -378,6 +381,7 @@ MonthArchiveView
* :attr:`~django.views.generic.list.MultipleObjectMixin.model` * :attr:`~django.views.generic.list.MultipleObjectMixin.model`
* :attr:`~django.views.generic.dates.MonthMixin.month` [:meth:`~django.views.generic.dates.MonthMixin.get_month`] * :attr:`~django.views.generic.dates.MonthMixin.month` [:meth:`~django.views.generic.dates.MonthMixin.get_month`]
* :attr:`~django.views.generic.dates.MonthMixin.month_format` [:meth:`~django.views.generic.dates.MonthMixin.get_month_format`] * :attr:`~django.views.generic.dates.MonthMixin.month_format` [:meth:`~django.views.generic.dates.MonthMixin.get_month_format`]
* :attr:`~django.views.generic.list.MultipleObjectMixin.ordering` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_ordering`]
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginate_by` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_paginate_by`] * :attr:`~django.views.generic.list.MultipleObjectMixin.paginate_by` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_paginate_by`]
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginate_orphans` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_paginate_orphans`] * :attr:`~django.views.generic.list.MultipleObjectMixin.paginate_orphans` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_paginate_orphans`]
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginator_class` * :attr:`~django.views.generic.list.MultipleObjectMixin.paginator_class`
@ -418,6 +422,7 @@ WeekArchiveView
* :attr:`~django.views.generic.dates.DateMixin.date_field` [:meth:`~django.views.generic.dates.DateMixin.get_date_field`] * :attr:`~django.views.generic.dates.DateMixin.date_field` [:meth:`~django.views.generic.dates.DateMixin.get_date_field`]
* :attr:`~django.views.generic.base.View.http_method_names` * :attr:`~django.views.generic.base.View.http_method_names`
* :attr:`~django.views.generic.list.MultipleObjectMixin.model` * :attr:`~django.views.generic.list.MultipleObjectMixin.model`
* :attr:`~django.views.generic.list.MultipleObjectMixin.ordering` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_ordering`]
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginate_by` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_paginate_by`] * :attr:`~django.views.generic.list.MultipleObjectMixin.paginate_by` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_paginate_by`]
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginate_orphans` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_paginate_orphans`] * :attr:`~django.views.generic.list.MultipleObjectMixin.paginate_orphans` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_paginate_orphans`]
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginator_class` * :attr:`~django.views.generic.list.MultipleObjectMixin.paginator_class`
@ -462,6 +467,7 @@ DayArchiveView
* :attr:`~django.views.generic.list.MultipleObjectMixin.model` * :attr:`~django.views.generic.list.MultipleObjectMixin.model`
* :attr:`~django.views.generic.dates.MonthMixin.month` [:meth:`~django.views.generic.dates.MonthMixin.get_month`] * :attr:`~django.views.generic.dates.MonthMixin.month` [:meth:`~django.views.generic.dates.MonthMixin.get_month`]
* :attr:`~django.views.generic.dates.MonthMixin.month_format` [:meth:`~django.views.generic.dates.MonthMixin.get_month_format`] * :attr:`~django.views.generic.dates.MonthMixin.month_format` [:meth:`~django.views.generic.dates.MonthMixin.get_month_format`]
* :attr:`~django.views.generic.list.MultipleObjectMixin.ordering` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_ordering`]
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginate_by` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_paginate_by`] * :attr:`~django.views.generic.list.MultipleObjectMixin.paginate_by` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_paginate_by`]
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginate_orphans` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_paginate_orphans`] * :attr:`~django.views.generic.list.MultipleObjectMixin.paginate_orphans` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_paginate_orphans`]
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginator_class` * :attr:`~django.views.generic.list.MultipleObjectMixin.paginator_class`
@ -508,6 +514,7 @@ TodayArchiveView
* :attr:`~django.views.generic.list.MultipleObjectMixin.model` * :attr:`~django.views.generic.list.MultipleObjectMixin.model`
* :attr:`~django.views.generic.dates.MonthMixin.month` [:meth:`~django.views.generic.dates.MonthMixin.get_month`] * :attr:`~django.views.generic.dates.MonthMixin.month` [:meth:`~django.views.generic.dates.MonthMixin.get_month`]
* :attr:`~django.views.generic.dates.MonthMixin.month_format` [:meth:`~django.views.generic.dates.MonthMixin.get_month_format`] * :attr:`~django.views.generic.dates.MonthMixin.month_format` [:meth:`~django.views.generic.dates.MonthMixin.get_month_format`]
* :attr:`~django.views.generic.list.MultipleObjectMixin.ordering` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_ordering`]
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginate_by` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_paginate_by`] * :attr:`~django.views.generic.list.MultipleObjectMixin.paginate_by` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_paginate_by`]
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginate_orphans` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_paginate_orphans`] * :attr:`~django.views.generic.list.MultipleObjectMixin.paginate_orphans` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_paginate_orphans`]
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginator_class` * :attr:`~django.views.generic.list.MultipleObjectMixin.paginator_class`

View File

@ -71,6 +71,13 @@ MultipleObjectMixin
retrieve it with :meth:`get_queryset` which takes care of the retrieve it with :meth:`get_queryset` which takes care of the
cloning behind the scenes. cloning behind the scenes.
.. attribute:: ordering
.. versionadded:: 1.8
A string or list of strings specifying the ordering to apply to the ``queryset``.
Valid values are the same as those for :meth:`~django.db.models.query.QuerySet.order_by`.
.. attribute:: paginate_by .. attribute:: paginate_by
An integer specifying how many objects should be displayed per page. If An integer specifying how many objects should be displayed per page. If
@ -110,6 +117,15 @@ MultipleObjectMixin
Get the list of items for this view. This must be an iterable and may Get the list of items for this view. This must be an iterable and may
be a queryset (in which queryset-specific behavior will be enabled). be a queryset (in which queryset-specific behavior will be enabled).
.. method:: get_ordering()
.. versionadded:: 1.8
Returns a string (or iterable of strings) that defines the ordering that
will be applied to the ``queryset``.
Returns :attr:`ordering` by default.
.. method:: paginate_queryset(queryset, page_size) .. method:: paginate_queryset(queryset, page_size)
Returns a 4-tuple containing (``paginator``, ``page``, ``object_list``, Returns a 4-tuple containing (``paginator``, ``page``, ``object_list``,

View File

@ -175,6 +175,15 @@ Forms
will also update ``UploadedFile.content_type`` with the image's content type will also update ``UploadedFile.content_type`` with the image's content type
as determined by Pillow. as determined by Pillow.
Generic Views
^^^^^^^^^^^^^
* Generic views that use :class:`~django.views.generic.list.MultipleObjectMixin`
may now specify the ordering applied to the
:attr:`~django.views.generic.list.MultipleObjectMixin.queryset` by setting
:attr:`~django.views.generic.list.MultipleObjectMixin.ordering` or overriding
:meth:`~django.views.generic.list.MultipleObjectMixin.get_ordering()`.
Internationalization Internationalization
^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^

View File

@ -120,6 +120,22 @@ class ArchiveIndexViewTests(TestCase):
self.assertEqual(res.status_code, 200) self.assertEqual(res.status_code, 200)
self.assertEqual(list(res.context['date_list']), list(reversed(sorted(res.context['date_list'])))) self.assertEqual(list(res.context['date_list']), list(reversed(sorted(res.context['date_list']))))
def test_archive_view_custom_sorting(self):
Book.objects.create(name="Zebras for Dummies", pages=600, pubdate=datetime.date(2007, 5, 1))
res = self.client.get('/dates/books/sortedbyname/')
self.assertEqual(res.status_code, 200)
self.assertEqual(list(res.context['date_list']), list(Book.objects.dates('pubdate', 'year', 'DESC')))
self.assertEqual(list(res.context['latest']), list(Book.objects.order_by('name').all()))
self.assertTemplateUsed(res, 'generic_views/book_archive.html')
def test_archive_view_custom_sorting_dec(self):
Book.objects.create(name="Zebras for Dummies", pages=600, pubdate=datetime.date(2007, 5, 1))
res = self.client.get('/dates/books/sortedbynamedec/')
self.assertEqual(res.status_code, 200)
self.assertEqual(list(res.context['date_list']), list(Book.objects.dates('pubdate', 'year', 'DESC')))
self.assertEqual(list(res.context['latest']), list(Book.objects.order_by('-name').all()))
self.assertTemplateUsed(res, 'generic_views/book_archive.html')
@override_settings(ROOT_URLCONF='generic_views.urls') @override_settings(ROOT_URLCONF='generic_views.urls')
class YearArchiveViewTests(TestCase): class YearArchiveViewTests(TestCase):
@ -178,6 +194,26 @@ class YearArchiveViewTests(TestCase):
self.assertEqual(list(res.context['object_list']), list(Book.objects.filter(pubdate__year=2006))) self.assertEqual(list(res.context['object_list']), list(Book.objects.filter(pubdate__year=2006)))
self.assertTemplateUsed(res, 'generic_views/book_archive_year.html') self.assertTemplateUsed(res, 'generic_views/book_archive_year.html')
def test_year_view_custom_sort_order(self):
# Zebras comes after Dreaming by name, but before on '-pubdate' which is the default sorting
Book.objects.create(name="Zebras for Dummies", pages=600, pubdate=datetime.date(2006, 9, 1))
res = self.client.get('/dates/books/2006/sortedbyname/')
self.assertEqual(res.status_code, 200)
self.assertEqual(list(res.context['date_list']), [datetime.date(2006, 5, 1), datetime.date(2006, 9, 1)])
self.assertEqual(list(res.context['book_list']), list(Book.objects.filter(pubdate__year=2006).order_by('name')))
self.assertEqual(list(res.context['object_list']), list(Book.objects.filter(pubdate__year=2006).order_by('name')))
self.assertTemplateUsed(res, 'generic_views/book_archive_year.html')
def test_year_view_two_custom_sort_orders(self):
Book.objects.create(name="Zebras for Dummies", pages=300, pubdate=datetime.date(2006, 9, 1))
Book.objects.create(name="Hunting Hippos", pages=400, pubdate=datetime.date(2006, 3, 1))
res = self.client.get('/dates/books/2006/sortedbypageandnamedec/')
self.assertEqual(res.status_code, 200)
self.assertEqual(list(res.context['date_list']), [datetime.date(2006, 3, 1), datetime.date(2006, 5, 1), datetime.date(2006, 9, 1)])
self.assertEqual(list(res.context['book_list']), list(Book.objects.filter(pubdate__year=2006).order_by('pages', '-name')))
self.assertEqual(list(res.context['object_list']), list(Book.objects.filter(pubdate__year=2006).order_by('pages', '-name')))
self.assertTemplateUsed(res, 'generic_views/book_archive_year.html')
def test_year_view_invalid_pattern(self): def test_year_view_invalid_pattern(self):
res = self.client.get('/dates/books/no_year/') res = self.client.get('/dates/books/no_year/')
self.assertEqual(res.status_code, 404) self.assertEqual(res.status_code, 404)

View File

@ -1,11 +1,13 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import datetime
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from django.test import TestCase, override_settings from django.test import TestCase, override_settings
from django.views.generic.base import View from django.views.generic.base import View
from django.utils.encoding import force_str from django.utils.encoding import force_str
from .models import Author, Artist from .models import Author, Artist, Book
@override_settings(ROOT_URLCONF='generic_views.urls') @override_settings(ROOT_URLCONF='generic_views.urls')
@ -200,6 +202,20 @@ class ListViewTests(TestCase):
with self.assertNumQueries(3): with self.assertNumQueries(3):
self.client.get('/list/authors/notempty/paginated/') self.client.get('/list/authors/notempty/paginated/')
def test_explicitly_ordered_list_view(self):
Book.objects.create(name="Zebras for Dummies", pages=800, pubdate=datetime.date(2006, 9, 1))
res = self.client.get('/list/books/sorted/')
self.assertEqual(res.status_code, 200)
self.assertEqual(res.context['object_list'][0].name, '2066')
self.assertEqual(res.context['object_list'][1].name, 'Dreaming in Code')
self.assertEqual(res.context['object_list'][2].name, 'Zebras for Dummies')
res = self.client.get('/list/books/sortedbypagesandnamedec/')
self.assertEqual(res.status_code, 200)
self.assertEqual(res.context['object_list'][0].name, 'Dreaming in Code')
self.assertEqual(res.context['object_list'][1].name, 'Zebras for Dummies')
self.assertEqual(res.context['object_list'][2].name, '2066')
@override_settings(DEBUG=True) @override_settings(DEBUG=True)
def test_paginated_list_view_returns_useful_message_on_invalid_page(self): def test_paginated_list_view_returns_useful_message_on_invalid_page(self):
# test for #19240 # test for #19240

View File

@ -121,6 +121,11 @@ urlpatterns = [
views.BookArchive.as_view(date_list_period='month')), views.BookArchive.as_view(date_list_period='month')),
url(r'^dates/booksignings/$', url(r'^dates/booksignings/$',
views.BookSigningArchive.as_view()), views.BookSigningArchive.as_view()),
url(r'^dates/books/sortedbyname/$',
views.BookArchive.as_view(ordering='name')),
url(r'^dates/books/sortedbynamedec/$',
views.BookArchive.as_view(ordering='-name')),
# ListView # ListView
url(r'^list/dict/$', url(r'^list/dict/$',
@ -159,6 +164,10 @@ urlpatterns = [
views.AuthorList.as_view(paginate_by=30, page_kwarg='pagina')), views.AuthorList.as_view(paginate_by=30, page_kwarg='pagina')),
url(r'^list/authors/paginated/custom_constructor/$', url(r'^list/authors/paginated/custom_constructor/$',
views.AuthorListCustomPaginator.as_view()), views.AuthorListCustomPaginator.as_view()),
url(r'^list/books/sorted/$',
views.BookList.as_view(ordering='name')),
url(r'^list/books/sortedbypagesandnamedec/$',
views.BookList.as_view(ordering=('pages', '-name'))),
# YearArchiveView # YearArchiveView
# Mixing keyword and positional captures below is intentional; the views # Mixing keyword and positional captures below is intentional; the views
@ -173,6 +182,10 @@ urlpatterns = [
views.BookYearArchive.as_view(allow_future=True)), views.BookYearArchive.as_view(allow_future=True)),
url(r'^dates/books/(?P<year>[0-9]{4})/paginated/$', url(r'^dates/books/(?P<year>[0-9]{4})/paginated/$',
views.BookYearArchive.as_view(make_object_list=True, paginate_by=30)), views.BookYearArchive.as_view(make_object_list=True, paginate_by=30)),
url(r'^dates/books/(?P<year>\d{4})/sortedbyname/$',
views.BookYearArchive.as_view(make_object_list=True, ordering='name')),
url(r'^dates/books/(?P<year>\d{4})/sortedbypageandnamedec/$',
views.BookYearArchive.as_view(make_object_list=True, ordering=('pages', '-name'))),
url(r'^dates/books/no_year/$', url(r'^dates/books/no_year/$',
views.BookYearArchive.as_view()), views.BookYearArchive.as_view()),
url(r'^dates/books/(?P<year>[0-9]{4})/reverse/$', url(r'^dates/books/(?P<year>[0-9]{4})/reverse/$',

View File

@ -57,6 +57,10 @@ class AuthorList(generic.ListView):
queryset = Author.objects.all() queryset = Author.objects.all()
class BookList(generic.ListView):
model = Book
class CustomPaginator(Paginator): class CustomPaginator(Paginator):
def __init__(self, queryset, page_size, orphans=0, allow_empty_first_page=True): def __init__(self, queryset, page_size, orphans=0, allow_empty_first_page=True):
super(CustomPaginator, self).__init__( super(CustomPaginator, self).__init__(