mirror of https://github.com/django/django.git
Fixed #18355 -- Added ordering options to list based generic views.
Added MultipleObjectMixin.ordering and get_ordering(). Refs #21450.
This commit is contained in:
parent
0ad4672c0f
commit
2724cdbff6
|
@ -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():
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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`
|
||||||
|
|
|
@ -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``,
|
||||||
|
|
|
@ -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
|
||||||
^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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/$',
|
||||||
|
|
|
@ -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__(
|
||||||
|
|
Loading…
Reference in New Issue