Fixed #13607 -- Auto-initialize admin's date hierarchy links intelligently. Thanks, Simon Meers.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@14879 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Jannis Leidel 2010-12-12 22:54:50 +00:00
parent 88ff656731
commit 2c2209b473
4 changed files with 120 additions and 1 deletions

View File

@ -231,6 +231,16 @@ def date_hierarchy(cl):
link = lambda d: cl.get_query_string(d, [field_generic]) link = lambda d: cl.get_query_string(d, [field_generic])
if not (year_lookup or month_lookup or day_lookup):
# select appropriate start level
date_range = cl.query_set.aggregate(first=models.Min(field_name),
last=models.Max(field_name))
if date_range['first'] and date_range['last']:
if date_range['first'].year == date_range['last'].year:
year_lookup = date_range['first'].year
if date_range['first'].month == date_range['last'].month:
month_lookup = date_range['first'].month
if year_lookup and month_lookup and day_lookup: if year_lookup and month_lookup and day_lookup:
day = datetime.date(int(year_lookup), int(month_lookup), int(day_lookup)) day = datetime.date(int(year_lookup), int(month_lookup), int(day_lookup))
return { return {

View File

@ -107,6 +107,12 @@ subclass::
date_hierarchy = 'pub_date' date_hierarchy = 'pub_date'
.. versionadded:: 1.3
This will intelligently populate itself based on available data,
e.g. if all the dates are in one month, it'll show the day-level
drill-down only.
.. attribute:: ModelAdmin.form .. attribute:: ModelAdmin.form
By default a ``ModelForm`` is dynamically created for your model. It is By default a ``ModelForm`` is dynamically created for your model. It is

View File

@ -320,7 +320,7 @@ class Podcast(Media):
class PodcastAdmin(admin.ModelAdmin): class PodcastAdmin(admin.ModelAdmin):
list_display = ('name', 'release_date') list_display = ('name', 'release_date')
list_editable = ('release_date',) list_editable = ('release_date',)
date_hierarchy = 'release_date'
ordering = ('name',) ordering = ('name',)
class Vodcast(Media): class Vodcast(Media):

View File

@ -6,6 +6,7 @@ import datetime
from django.conf import settings from django.conf import settings
from django.core import mail from django.core import mail
from django.core.files import temp as tempfile from django.core.files import temp as tempfile
from django.core.urlresolvers import reverse
# Register auth models with the admin. # Register auth models with the admin.
from django.contrib.auth import REDIRECT_FIELD_NAME, admin from django.contrib.auth import REDIRECT_FIELD_NAME, admin
from django.contrib.auth.models import User, Permission, UNUSABLE_PASSWORD from django.contrib.auth.models import User, Permission, UNUSABLE_PASSWORD
@ -2395,3 +2396,105 @@ class ValidXHTMLTests(TestCase):
response = self.client.get('/test_admin/%s/admin_views/' % self.urlbit) response = self.client.get('/test_admin/%s/admin_views/' % self.urlbit)
self.assertFalse(' lang=""' in response.content) self.assertFalse(' lang=""' in response.content)
self.assertFalse(' xml:lang=""' in response.content) self.assertFalse(' xml:lang=""' in response.content)
class DateHierarchyTests(TestCase):
fixtures = ['admin-views-users.xml']
def setUp(self):
self.client.login(username='super', password='secret')
def assert_contains_year_link(self, response, date):
self.assertContains(response, '?release_date__year=%d"' % (date.year,))
def assert_contains_month_link(self, response, date):
self.assertContains(
response, '?release_date__year=%d&release_date__month=%d"' % (
date.year, date.month))
def assert_contains_day_link(self, response, date):
self.assertContains(
response, '?release_date__year=%d&'
'release_date__month=%d&release_date__day=%d"' % (
date.year, date.month, date.day))
def test_empty(self):
"""
Ensure that no date hierarchy links display with empty changelist.
"""
response = self.client.get(
reverse('admin:admin_views_podcast_changelist'))
self.assertNotContains(response, 'release_date__year=')
self.assertNotContains(response, 'release_date__month=')
self.assertNotContains(response, 'release_date__day=')
def test_single(self):
"""
Ensure that single day-level date hierarchy appears for single object.
"""
DATE = datetime.date(2000, 6, 30)
Podcast.objects.create(release_date=DATE)
response = self.client.get(
reverse('admin:admin_views_podcast_changelist'))
self.assert_contains_day_link(response, DATE)
def test_within_month(self):
"""
Ensure that day-level links appear for changelist within single month.
"""
DATES = (datetime.date(2000, 6, 30),
datetime.date(2000, 6, 15),
datetime.date(2000, 6, 3))
for date in DATES:
Podcast.objects.create(release_date=date)
response = self.client.get(
reverse('admin:admin_views_podcast_changelist'))
for date in DATES:
self.assert_contains_day_link(response, date)
def test_within_year(self):
"""
Ensure that month-level links appear for changelist within single year.
"""
DATES = (datetime.date(2000, 1, 30),
datetime.date(2000, 3, 15),
datetime.date(2000, 5, 3))
for date in DATES:
Podcast.objects.create(release_date=date)
response = self.client.get(
reverse('admin:admin_views_podcast_changelist'))
# no day-level links
self.assertNotContains(response, 'release_date__day=')
for date in DATES:
self.assert_contains_month_link(response, date)
def test_multiple_years(self):
"""
Ensure that year-level links appear for year-spanning changelist.
"""
DATES = (datetime.date(2001, 1, 30),
datetime.date(2003, 3, 15),
datetime.date(2005, 5, 3))
for date in DATES:
Podcast.objects.create(release_date=date)
response = self.client.get(
reverse('admin:admin_views_podcast_changelist'))
# no day/month-level links
self.assertNotContains(response, 'release_date__day=')
self.assertNotContains(response, 'release_date__month=')
for date in DATES:
self.assert_contains_year_link(response, date)
# and make sure GET parameters still behave correctly
for date in DATES:
response = self.client.get(
'%s?release_date__year=%d' % (
reverse('admin:admin_views_podcast_changelist'),
date.year))
self.assert_contains_month_link(response, date)
response = self.client.get(
'%s?release_date__year=%d&release_date__month=%d' % (
reverse('admin:admin_views_podcast_changelist'),
date.year, date.month))
self.assert_contains_day_link(response, date)