mirror of https://github.com/django/django.git
Fixed #16218 -- date_list order in generic CBVs.
Thanks nnrcschmdt for the report and bpeschier for the initial version of the patch.
This commit is contained in:
parent
59afc18f37
commit
baa33cd8fa
|
@ -377,7 +377,7 @@ class BaseDateListView(MultipleObjectMixin, DateMixin, View):
|
||||||
"""
|
"""
|
||||||
return self.date_list_period
|
return self.date_list_period
|
||||||
|
|
||||||
def get_date_list(self, queryset, date_type=None):
|
def get_date_list(self, queryset, date_type=None, ordering='ASC'):
|
||||||
"""
|
"""
|
||||||
Get a date list by calling `queryset.dates()`, checking along the way
|
Get a date list by calling `queryset.dates()`, checking along the way
|
||||||
for empty lists that aren't allowed.
|
for empty lists that aren't allowed.
|
||||||
|
@ -387,7 +387,7 @@ class BaseDateListView(MultipleObjectMixin, DateMixin, View):
|
||||||
if date_type is None:
|
if date_type is None:
|
||||||
date_type = self.get_date_list_period()
|
date_type = self.get_date_list_period()
|
||||||
|
|
||||||
date_list = queryset.dates(date_field, date_type)[::-1]
|
date_list = queryset.dates(date_field, date_type, ordering)
|
||||||
if date_list is not None and not date_list and not allow_empty:
|
if date_list is not None and not date_list and not allow_empty:
|
||||||
name = force_text(queryset.model._meta.verbose_name_plural)
|
name = force_text(queryset.model._meta.verbose_name_plural)
|
||||||
raise Http404(_("No %(verbose_name_plural)s available") %
|
raise Http404(_("No %(verbose_name_plural)s available") %
|
||||||
|
@ -409,7 +409,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(ordering='-%s' % self.get_date_field())
|
||||||
date_list = self.get_date_list(qs)
|
date_list = self.get_date_list(qs, ordering='DESC')
|
||||||
|
|
||||||
if not date_list:
|
if not date_list:
|
||||||
qs = qs.none()
|
qs = qs.none()
|
||||||
|
|
|
@ -318,12 +318,16 @@ BaseDateListView
|
||||||
Returns the aggregation period for ``date_list``. Returns
|
Returns the aggregation period for ``date_list``. Returns
|
||||||
:attr:`~BaseDateListView.date_list_period` by default.
|
:attr:`~BaseDateListView.date_list_period` by default.
|
||||||
|
|
||||||
.. method:: get_date_list(queryset, date_type=None)
|
.. method:: get_date_list(queryset, date_type=None, ordering='ASC')
|
||||||
|
|
||||||
Returns the list of dates of type ``date_type`` for which ``queryset``
|
Returns the list of dates of type ``date_type`` for which ``queryset``
|
||||||
contains entries. For example, ``get_date_list(qs, 'year')`` will
|
contains entries. For example, ``get_date_list(qs, 'year')`` will
|
||||||
return the list of years for which ``qs`` has entries. If
|
return the list of years for which ``qs`` has entries. If
|
||||||
``date_type`` isn't provided, the result of
|
``date_type`` isn't provided, the result of
|
||||||
:meth:`BaseDateListView.get_date_list_period` is used. See
|
:meth:`~BaseDateListView.get_date_list_period` is used. ``date_type``
|
||||||
:meth:`~django.db.models.query.QuerySet.dates()` for the ways that the
|
and ``ordering`` are simply passed to
|
||||||
``date_type`` argument can be used.
|
:meth:`QuerySet.dates()<django.db.models.query.QuerySet.dates>`.
|
||||||
|
|
||||||
|
.. versionchanged:: 1.5
|
||||||
|
The ``ordering`` parameter was added, and the default order was
|
||||||
|
changed to ascending.
|
||||||
|
|
|
@ -152,6 +152,21 @@ year|date:"Y" }}``.
|
||||||
``next_year`` and ``previous_year`` were also added in the context. They are
|
``next_year`` and ``previous_year`` were also added in the context. They are
|
||||||
calculated according to ``allow_empty`` and ``allow_future``.
|
calculated according to ``allow_empty`` and ``allow_future``.
|
||||||
|
|
||||||
|
Context in year and month archive class-based views
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
:class:`~django.views.generic.dates.YearArchiveView` and
|
||||||
|
:class:`~django.views.generic.dates.MonthArchiveView` were documented to
|
||||||
|
provide a ``date_list`` sorted in ascending order in the context, like their
|
||||||
|
function-based predecessors, but it actually was in descending order. In 1.5,
|
||||||
|
the documented order was restored. You may want to add (or remove) the
|
||||||
|
``reversed`` keyword when you're iterating on ``date_list`` in a template::
|
||||||
|
|
||||||
|
{% for date in date_list reversed %}
|
||||||
|
|
||||||
|
:class:`~django.views.generic.dates.ArchiveIndexView` still provides a
|
||||||
|
``date_list`` in descending order.
|
||||||
|
|
||||||
Context in TemplateView
|
Context in TemplateView
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
|
|
@ -23,29 +23,30 @@ requires_tz_support = skipUnless(TZ_SUPPORT,
|
||||||
"time zone, but your operating system isn't able to do that.")
|
"time zone, but your operating system isn't able to do that.")
|
||||||
|
|
||||||
|
|
||||||
|
def _make_books(n, base_date):
|
||||||
|
for i in range(n):
|
||||||
|
b = Book.objects.create(
|
||||||
|
name='Book %d' % i,
|
||||||
|
slug='book-%d' % i,
|
||||||
|
pages=100+i,
|
||||||
|
pubdate=base_date - datetime.timedelta(days=i))
|
||||||
|
|
||||||
class ArchiveIndexViewTests(TestCase):
|
class ArchiveIndexViewTests(TestCase):
|
||||||
fixtures = ['generic-views-test-data.json']
|
fixtures = ['generic-views-test-data.json']
|
||||||
urls = 'regressiontests.generic_views.urls'
|
urls = 'regressiontests.generic_views.urls'
|
||||||
|
|
||||||
def _make_books(self, n, base_date):
|
|
||||||
for i in range(n):
|
|
||||||
b = Book.objects.create(
|
|
||||||
name='Book %d' % i,
|
|
||||||
slug='book-%d' % i,
|
|
||||||
pages=100+i,
|
|
||||||
pubdate=base_date - datetime.timedelta(days=1))
|
|
||||||
|
|
||||||
def test_archive_view(self):
|
def test_archive_view(self):
|
||||||
res = self.client.get('/dates/books/')
|
res = self.client.get('/dates/books/')
|
||||||
self.assertEqual(res.status_code, 200)
|
self.assertEqual(res.status_code, 200)
|
||||||
self.assertEqual(res.context['date_list'], Book.objects.dates('pubdate', 'year')[::-1])
|
self.assertEqual(list(res.context['date_list']), list(Book.objects.dates('pubdate', 'year', 'DESC')))
|
||||||
self.assertEqual(list(res.context['latest']), list(Book.objects.all()))
|
self.assertEqual(list(res.context['latest']), list(Book.objects.all()))
|
||||||
self.assertTemplateUsed(res, 'generic_views/book_archive.html')
|
self.assertTemplateUsed(res, 'generic_views/book_archive.html')
|
||||||
|
|
||||||
def test_archive_view_context_object_name(self):
|
def test_archive_view_context_object_name(self):
|
||||||
res = self.client.get('/dates/books/context_object_name/')
|
res = self.client.get('/dates/books/context_object_name/')
|
||||||
self.assertEqual(res.status_code, 200)
|
self.assertEqual(res.status_code, 200)
|
||||||
self.assertEqual(res.context['date_list'], Book.objects.dates('pubdate', 'year')[::-1])
|
self.assertEqual(list(res.context['date_list']), list(Book.objects.dates('pubdate', 'year', 'DESC')))
|
||||||
self.assertEqual(list(res.context['thingies']), list(Book.objects.all()))
|
self.assertEqual(list(res.context['thingies']), list(Book.objects.all()))
|
||||||
self.assertFalse('latest' in res.context)
|
self.assertFalse('latest' in res.context)
|
||||||
self.assertTemplateUsed(res, 'generic_views/book_archive.html')
|
self.assertTemplateUsed(res, 'generic_views/book_archive.html')
|
||||||
|
@ -65,14 +66,14 @@ class ArchiveIndexViewTests(TestCase):
|
||||||
def test_archive_view_template(self):
|
def test_archive_view_template(self):
|
||||||
res = self.client.get('/dates/books/template_name/')
|
res = self.client.get('/dates/books/template_name/')
|
||||||
self.assertEqual(res.status_code, 200)
|
self.assertEqual(res.status_code, 200)
|
||||||
self.assertEqual(res.context['date_list'], Book.objects.dates('pubdate', 'year')[::-1])
|
self.assertEqual(list(res.context['date_list']), list(Book.objects.dates('pubdate', 'year', 'DESC')))
|
||||||
self.assertEqual(list(res.context['latest']), list(Book.objects.all()))
|
self.assertEqual(list(res.context['latest']), list(Book.objects.all()))
|
||||||
self.assertTemplateUsed(res, 'generic_views/list.html')
|
self.assertTemplateUsed(res, 'generic_views/list.html')
|
||||||
|
|
||||||
def test_archive_view_template_suffix(self):
|
def test_archive_view_template_suffix(self):
|
||||||
res = self.client.get('/dates/books/template_name_suffix/')
|
res = self.client.get('/dates/books/template_name_suffix/')
|
||||||
self.assertEqual(res.status_code, 200)
|
self.assertEqual(res.status_code, 200)
|
||||||
self.assertEqual(res.context['date_list'], Book.objects.dates('pubdate', 'year')[::-1])
|
self.assertEqual(list(res.context['date_list']), list(Book.objects.dates('pubdate', 'year', 'DESC')))
|
||||||
self.assertEqual(list(res.context['latest']), list(Book.objects.all()))
|
self.assertEqual(list(res.context['latest']), list(Book.objects.all()))
|
||||||
self.assertTemplateUsed(res, 'generic_views/book_detail.html')
|
self.assertTemplateUsed(res, 'generic_views/book_detail.html')
|
||||||
|
|
||||||
|
@ -82,13 +83,13 @@ class ArchiveIndexViewTests(TestCase):
|
||||||
def test_archive_view_by_month(self):
|
def test_archive_view_by_month(self):
|
||||||
res = self.client.get('/dates/books/by_month/')
|
res = self.client.get('/dates/books/by_month/')
|
||||||
self.assertEqual(res.status_code, 200)
|
self.assertEqual(res.status_code, 200)
|
||||||
self.assertEqual(res.context['date_list'], Book.objects.dates('pubdate', 'month')[::-1])
|
self.assertEqual(list(res.context['date_list']), list(Book.objects.dates('pubdate', 'month', 'DESC')))
|
||||||
|
|
||||||
def test_paginated_archive_view(self):
|
def test_paginated_archive_view(self):
|
||||||
self._make_books(20, base_date=datetime.date.today())
|
_make_books(20, base_date=datetime.date.today())
|
||||||
res = self.client.get('/dates/books/paginated/')
|
res = self.client.get('/dates/books/paginated/')
|
||||||
self.assertEqual(res.status_code, 200)
|
self.assertEqual(res.status_code, 200)
|
||||||
self.assertEqual(res.context['date_list'], Book.objects.dates('pubdate', 'year')[::-1])
|
self.assertEqual(list(res.context['date_list']), list(Book.objects.dates('pubdate', 'year', 'DESC')))
|
||||||
self.assertEqual(list(res.context['latest']), list(Book.objects.all()[0:10]))
|
self.assertEqual(list(res.context['latest']), list(Book.objects.all()[0:10]))
|
||||||
self.assertTemplateUsed(res, 'generic_views/book_archive.html')
|
self.assertTemplateUsed(res, 'generic_views/book_archive.html')
|
||||||
|
|
||||||
|
@ -99,7 +100,7 @@ class ArchiveIndexViewTests(TestCase):
|
||||||
|
|
||||||
def test_paginated_archive_view_does_not_load_entire_table(self):
|
def test_paginated_archive_view_does_not_load_entire_table(self):
|
||||||
# Regression test for #18087
|
# Regression test for #18087
|
||||||
self._make_books(20, base_date=datetime.date.today())
|
_make_books(20, base_date=datetime.date.today())
|
||||||
# 1 query for years list + 1 query for books
|
# 1 query for years list + 1 query for books
|
||||||
with self.assertNumQueries(2):
|
with self.assertNumQueries(2):
|
||||||
self.client.get('/dates/books/')
|
self.client.get('/dates/books/')
|
||||||
|
@ -124,6 +125,13 @@ class ArchiveIndexViewTests(TestCase):
|
||||||
res = self.client.get('/dates/booksignings/')
|
res = self.client.get('/dates/booksignings/')
|
||||||
self.assertEqual(res.status_code, 200)
|
self.assertEqual(res.status_code, 200)
|
||||||
|
|
||||||
|
def test_date_list_order(self):
|
||||||
|
"""date_list should be sorted descending in index"""
|
||||||
|
_make_books(5, base_date=datetime.date(2011, 12, 25))
|
||||||
|
res = self.client.get('/dates/books/')
|
||||||
|
self.assertEqual(res.status_code, 200)
|
||||||
|
self.assertEqual(list(res.context['date_list']), list(reversed(sorted(res.context['date_list']))))
|
||||||
|
|
||||||
|
|
||||||
class YearArchiveViewTests(TestCase):
|
class YearArchiveViewTests(TestCase):
|
||||||
fixtures = ['generic-views-test-data.json']
|
fixtures = ['generic-views-test-data.json']
|
||||||
|
@ -202,6 +210,12 @@ class YearArchiveViewTests(TestCase):
|
||||||
res = self.client.get('/dates/booksignings/2008/')
|
res = self.client.get('/dates/booksignings/2008/')
|
||||||
self.assertEqual(res.status_code, 200)
|
self.assertEqual(res.status_code, 200)
|
||||||
|
|
||||||
|
def test_date_list_order(self):
|
||||||
|
"""date_list should be sorted ascending in year view"""
|
||||||
|
_make_books(10, base_date=datetime.date(2011, 12, 25))
|
||||||
|
res = self.client.get('/dates/books/2011/')
|
||||||
|
self.assertEqual(list(res.context['date_list']), list(sorted(res.context['date_list'])))
|
||||||
|
|
||||||
|
|
||||||
class MonthArchiveViewTests(TestCase):
|
class MonthArchiveViewTests(TestCase):
|
||||||
fixtures = ['generic-views-test-data.json']
|
fixtures = ['generic-views-test-data.json']
|
||||||
|
@ -322,6 +336,12 @@ class MonthArchiveViewTests(TestCase):
|
||||||
res = self.client.get('/dates/booksignings/2008/apr/')
|
res = self.client.get('/dates/booksignings/2008/apr/')
|
||||||
self.assertEqual(res.status_code, 200)
|
self.assertEqual(res.status_code, 200)
|
||||||
|
|
||||||
|
def test_date_list_order(self):
|
||||||
|
"""date_list should be sorted ascending in month view"""
|
||||||
|
_make_books(10, base_date=datetime.date(2011, 12, 25))
|
||||||
|
res = self.client.get('/dates/books/2011/dec/')
|
||||||
|
self.assertEqual(list(res.context['date_list']), list(sorted(res.context['date_list'])))
|
||||||
|
|
||||||
|
|
||||||
class WeekArchiveViewTests(TestCase):
|
class WeekArchiveViewTests(TestCase):
|
||||||
fixtures = ['generic-views-test-data.json']
|
fixtures = ['generic-views-test-data.json']
|
||||||
|
|
Loading…
Reference in New Issue