Kaydet (Commit) 905bd7fb authored tarafından Aymeric Augustin's avatar Aymeric Augustin

Fixed #13196 -- Formatting in admin changelists.

Handled values returned by functions more like field values.
In particular, localized dates, times and datetimes properly,
and converted datetimes to the current timezone.
üst 7c27d156
import datetime import datetime
from django.contrib.admin.util import lookup_field, display_for_field, label_for_field from django.contrib.admin.util import (lookup_field, display_for_field,
display_for_value, label_for_field)
from django.contrib.admin.views.main import (ALL_VAR, EMPTY_CHANGELIST_VALUE, from django.contrib.admin.views.main import (ALL_VAR, EMPTY_CHANGELIST_VALUE,
ORDER_VAR, PAGE_VAR, SEARCH_VAR) ORDER_VAR, PAGE_VAR, SEARCH_VAR)
from django.contrib.admin.templatetags.admin_static import static from django.contrib.admin.templatetags.admin_static import static
...@@ -184,15 +185,15 @@ def items_for_result(cl, result, form): ...@@ -184,15 +185,15 @@ def items_for_result(cl, result, form):
boolean = getattr(attr, 'boolean', False) boolean = getattr(attr, 'boolean', False)
if boolean: if boolean:
allow_tags = True allow_tags = True
result_repr = _boolean_icon(value) result_repr = display_for_value(value, boolean)
else:
result_repr = smart_unicode(value)
# Strip HTML tags in the resulting text, except if the # Strip HTML tags in the resulting text, except if the
# function has an "allow_tags" attribute set to True. # function has an "allow_tags" attribute set to True.
if not allow_tags: if not allow_tags:
result_repr = escape(result_repr) result_repr = escape(result_repr)
else: else:
result_repr = mark_safe(result_repr) result_repr = mark_safe(result_repr)
if isinstance(value, (datetime.date, datetime.time)):
row_class = ' class="nowrap"'
else: else:
if isinstance(f.rel, models.ManyToOneRel): if isinstance(f.rel, models.ManyToOneRel):
field_val = getattr(result, f.name) field_val = getattr(result, f.name)
...@@ -202,9 +203,7 @@ def items_for_result(cl, result, form): ...@@ -202,9 +203,7 @@ def items_for_result(cl, result, form):
result_repr = escape(field_val) result_repr = escape(field_val)
else: else:
result_repr = display_for_field(value, f) result_repr = display_for_field(value, f)
if isinstance(f, models.DateField)\ if isinstance(f, (models.DateField, models.TimeField, models.ForeignKey)):
or isinstance(f, models.TimeField)\
or isinstance(f, models.ForeignKey):
row_class = ' class="nowrap"' row_class = ' class="nowrap"'
if force_unicode(result_repr) == '': if force_unicode(result_repr) == '':
result_repr = mark_safe(' ') result_repr = mark_safe(' ')
......
import datetime
import decimal
from django.db import models from django.db import models
from django.db.models.sql.constants import LOOKUP_SEP from django.db.models.sql.constants import LOOKUP_SEP
from django.db.models.deletion import Collector from django.db.models.deletion import Collector
...@@ -323,7 +326,7 @@ def display_for_field(value, field): ...@@ -323,7 +326,7 @@ def display_for_field(value, field):
return EMPTY_CHANGELIST_VALUE return EMPTY_CHANGELIST_VALUE
elif isinstance(field, models.DateTimeField): elif isinstance(field, models.DateTimeField):
return formats.localize(timezone.localtime(value)) return formats.localize(timezone.localtime(value))
elif isinstance(field, models.DateField) or isinstance(field, models.TimeField): elif isinstance(field, (models.DateField, models.TimeField)):
return formats.localize(value) return formats.localize(value)
elif isinstance(field, models.DecimalField): elif isinstance(field, models.DecimalField):
return formats.number_format(value, field.decimal_places) return formats.number_format(value, field.decimal_places)
...@@ -333,6 +336,24 @@ def display_for_field(value, field): ...@@ -333,6 +336,24 @@ def display_for_field(value, field):
return smart_unicode(value) return smart_unicode(value)
def display_for_value(value, boolean=False):
from django.contrib.admin.templatetags.admin_list import _boolean_icon
from django.contrib.admin.views.main import EMPTY_CHANGELIST_VALUE
if boolean:
return _boolean_icon(value)
elif value is None:
return EMPTY_CHANGELIST_VALUE
elif isinstance(value, datetime.datetime):
return formats.localize(timezone.localtime(value))
elif isinstance(value, (datetime.date, datetime.time)):
return formats.localize(value)
elif isinstance(value, (decimal.Decimal, float, int, long)):
return formats.number_format(value)
else:
return smart_unicode(value)
class NotRelationField(Exception): class NotRelationField(Exception):
pass pass
......
...@@ -3,8 +3,8 @@ from __future__ import absolute_import ...@@ -3,8 +3,8 @@ from __future__ import absolute_import
from django.contrib import admin from django.contrib import admin
from django.core.paginator import Paginator from django.core.paginator import Paginator
from .models import (Child, Parent, Genre, Band, Musician, Group, Quartet, from .models import (Event, Child, Parent, Genre, Band, Musician, Group,
Membership, ChordsMusician, ChordsBand, Invitation, Swallow) Quartet, Membership, ChordsMusician, ChordsBand, Invitation, Swallow)
site = admin.AdminSite(name="admin") site = admin.AdminSite(name="admin")
...@@ -15,6 +15,15 @@ class CustomPaginator(Paginator): ...@@ -15,6 +15,15 @@ class CustomPaginator(Paginator):
allow_empty_first_page=allow_empty_first_page) allow_empty_first_page=allow_empty_first_page)
class EventAdmin(admin.ModelAdmin):
list_display = ['event_date_func']
def event_date_func(self, event):
return event.date
site.register(Event, EventAdmin)
class ParentAdmin(admin.ModelAdmin): class ParentAdmin(admin.ModelAdmin):
list_filter = ['child__name'] list_filter = ['child__name']
search_fields = ['child__name'] search_fields = ['child__name']
......
from django.db import models from django.db import models
class Event(models.Model):
date = models.DateField()
class Parent(models.Model): class Parent(models.Model):
name = models.CharField(max_length=128) name = models.CharField(max_length=128)
......
from __future__ import absolute_import from __future__ import absolute_import
import datetime
from django.contrib import admin from django.contrib import admin
from django.contrib.admin.options import IncorrectLookupParameters from django.contrib.admin.options import IncorrectLookupParameters
from django.contrib.admin.views.main import ChangeList, SEARCH_VAR, ALL_VAR from django.contrib.admin.views.main import ChangeList, SEARCH_VAR, ALL_VAR
...@@ -7,14 +9,15 @@ from django.contrib.auth.models import User ...@@ -7,14 +9,15 @@ from django.contrib.auth.models import User
from django.template import Context, Template from django.template import Context, Template
from django.test import TestCase from django.test import TestCase
from django.test.client import RequestFactory from django.test.client import RequestFactory
from django.utils import formats
from .admin import (ChildAdmin, QuartetAdmin, BandAdmin, ChordsBandAdmin, from .admin import (ChildAdmin, QuartetAdmin, BandAdmin, ChordsBandAdmin,
GroupAdmin, ParentAdmin, DynamicListDisplayChildAdmin, GroupAdmin, ParentAdmin, DynamicListDisplayChildAdmin,
DynamicListDisplayLinksChildAdmin, CustomPaginationAdmin, DynamicListDisplayLinksChildAdmin, CustomPaginationAdmin,
FilteredChildAdmin, CustomPaginator, site as custom_site, FilteredChildAdmin, CustomPaginator, site as custom_site,
SwallowAdmin) SwallowAdmin)
from .models import (Child, Parent, Genre, Band, Musician, Group, Quartet, from .models import (Event, Child, Parent, Genre, Band, Musician, Group,
Membership, ChordsMusician, ChordsBand, Invitation, Swallow, Quartet, Membership, ChordsMusician, ChordsBand, Invitation, Swallow,
UnorderedObject, OrderedObject) UnorderedObject, OrderedObject)
...@@ -325,6 +328,19 @@ class ChangeListTests(TestCase): ...@@ -325,6 +328,19 @@ class ChangeListTests(TestCase):
self.assertEqual(cl.paginator.count, 30) self.assertEqual(cl.paginator.count, 30)
self.assertEqual(cl.paginator.page_range, [1, 2, 3]) self.assertEqual(cl.paginator.page_range, [1, 2, 3])
def test_computed_list_display_localization(self):
"""
Regression test for #13196: output of functions should be localized
in the changelist.
"""
User.objects.create_superuser(
username='super', email='super@localhost', password='secret')
self.client.login(username='super', password='secret')
event = Event.objects.create(date=datetime.date.today())
response = self.client.get('/admin/admin_changelist/event/')
self.assertContains(response, formats.localize(event.date))
self.assertNotContains(response, unicode(event.date))
def test_dynamic_list_display(self): def test_dynamic_list_display(self):
""" """
Regression tests for #14206: dynamic list_display support. Regression tests for #14206: dynamic list_display support.
...@@ -519,4 +535,4 @@ class ChangeListTests(TestCase): ...@@ -519,4 +535,4 @@ class ChangeListTests(TestCase):
OrderedObjectAdmin.ordering = ['-id', 'bool'] OrderedObjectAdmin.ordering = ['-id', 'bool']
check_results_order() check_results_order()
OrderedObjectAdmin.ordering = ['id', 'bool'] OrderedObjectAdmin.ordering = ['id', 'bool']
check_results_order(ascending=True) check_results_order(ascending=True)
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment