Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

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.
  • Loading branch information...
commit 905bd7fb44a0dbd0be0d455ab428c388714ae700 1 parent 7c27d15
Aymeric Augustin authored April 29, 2012
13  django/contrib/admin/templatetags/admin_list.py
... ...
@@ -1,6 +1,7 @@
1 1
 import datetime
2 2
 
3  
-from django.contrib.admin.util import lookup_field, display_for_field, label_for_field
  3
+from django.contrib.admin.util import (lookup_field, display_for_field,
  4
+    display_for_value, label_for_field)
4 5
 from django.contrib.admin.views.main import (ALL_VAR, EMPTY_CHANGELIST_VALUE,
5 6
     ORDER_VAR, PAGE_VAR, SEARCH_VAR)
6 7
 from django.contrib.admin.templatetags.admin_static import static
@@ -184,15 +185,15 @@ def items_for_result(cl, result, form):
184 185
                 boolean = getattr(attr, 'boolean', False)
185 186
                 if boolean:
186 187
                     allow_tags = True
187  
-                    result_repr = _boolean_icon(value)
188  
-                else:
189  
-                    result_repr = smart_unicode(value)
  188
+                result_repr = display_for_value(value, boolean)
190 189
                 # Strip HTML tags in the resulting text, except if the
191 190
                 # function has an "allow_tags" attribute set to True.
192 191
                 if not allow_tags:
193 192
                     result_repr = escape(result_repr)
194 193
                 else:
195 194
                     result_repr = mark_safe(result_repr)
  195
+                if isinstance(value, (datetime.date, datetime.time)):
  196
+                    row_class = ' class="nowrap"'
196 197
             else:
197 198
                 if isinstance(f.rel, models.ManyToOneRel):
198 199
                     field_val = getattr(result, f.name)
@@ -202,9 +203,7 @@ def items_for_result(cl, result, form):
202 203
                         result_repr = escape(field_val)
203 204
                 else:
204 205
                     result_repr = display_for_field(value, f)
205  
-                if isinstance(f, models.DateField)\
206  
-                or isinstance(f, models.TimeField)\
207  
-                or isinstance(f, models.ForeignKey):
  206
+                if isinstance(f, (models.DateField, models.TimeField, models.ForeignKey)):
208 207
                     row_class = ' class="nowrap"'
209 208
         if force_unicode(result_repr) == '':
210 209
             result_repr = mark_safe(' ')
23  django/contrib/admin/util.py
... ...
@@ -1,3 +1,6 @@
  1
+import datetime
  2
+import decimal
  3
+
1 4
 from django.db import models
2 5
 from django.db.models.sql.constants import LOOKUP_SEP
3 6
 from django.db.models.deletion import Collector
@@ -323,7 +326,7 @@ def display_for_field(value, field):
323 326
         return EMPTY_CHANGELIST_VALUE
324 327
     elif isinstance(field, models.DateTimeField):
325 328
         return formats.localize(timezone.localtime(value))
326  
-    elif isinstance(field, models.DateField) or isinstance(field, models.TimeField):
  329
+    elif isinstance(field, (models.DateField, models.TimeField)):
327 330
         return formats.localize(value)
328 331
     elif isinstance(field, models.DecimalField):
329 332
         return formats.number_format(value, field.decimal_places)
@@ -333,6 +336,24 @@ def display_for_field(value, field):
333 336
         return smart_unicode(value)
334 337
 
335 338
 
  339
+def display_for_value(value, boolean=False):
  340
+    from django.contrib.admin.templatetags.admin_list import _boolean_icon
  341
+    from django.contrib.admin.views.main import EMPTY_CHANGELIST_VALUE
  342
+
  343
+    if boolean:
  344
+        return _boolean_icon(value)
  345
+    elif value is None:
  346
+        return EMPTY_CHANGELIST_VALUE
  347
+    elif isinstance(value, datetime.datetime):
  348
+        return formats.localize(timezone.localtime(value))
  349
+    elif isinstance(value, (datetime.date, datetime.time)):
  350
+        return formats.localize(value)
  351
+    elif isinstance(value, (decimal.Decimal, float, int, long)):
  352
+        return formats.number_format(value)
  353
+    else:
  354
+        return smart_unicode(value)
  355
+
  356
+
336 357
 class NotRelationField(Exception):
337 358
     pass
338 359
 
13  tests/regressiontests/admin_changelist/admin.py
@@ -3,8 +3,8 @@
3 3
 from django.contrib import admin
4 4
 from django.core.paginator import Paginator
5 5
 
6  
-from .models import (Child, Parent, Genre, Band, Musician, Group, Quartet,
7  
-    Membership, ChordsMusician, ChordsBand, Invitation, Swallow)
  6
+from .models import (Event, Child, Parent, Genre, Band, Musician, Group,
  7
+    Quartet, Membership, ChordsMusician, ChordsBand, Invitation, Swallow)
8 8
 
9 9
 
10 10
 site = admin.AdminSite(name="admin")
@@ -15,6 +15,15 @@ def __init__(self, queryset, page_size, orphans=0, allow_empty_first_page=True):
15 15
             allow_empty_first_page=allow_empty_first_page)
16 16
 
17 17
 
  18
+class EventAdmin(admin.ModelAdmin):
  19
+    list_display = ['event_date_func']
  20
+
  21
+    def event_date_func(self, event):
  22
+        return event.date
  23
+
  24
+site.register(Event, EventAdmin)
  25
+
  26
+
18 27
 class ParentAdmin(admin.ModelAdmin):
19 28
     list_filter = ['child__name']
20 29
     search_fields = ['child__name']
2  tests/regressiontests/admin_changelist/models.py
... ...
@@ -1,5 +1,7 @@
1 1
 from django.db import models
2 2
 
  3
+class Event(models.Model):
  4
+    date = models.DateField()
3 5
 
4 6
 class Parent(models.Model):
5 7
     name = models.CharField(max_length=128)
22  tests/regressiontests/admin_changelist/tests.py
... ...
@@ -1,5 +1,7 @@
1 1
 from __future__ import absolute_import
2 2
 
  3
+import datetime
  4
+
3 5
 from django.contrib import admin
4 6
 from django.contrib.admin.options import IncorrectLookupParameters
5 7
 from django.contrib.admin.views.main import ChangeList, SEARCH_VAR, ALL_VAR
@@ -7,14 +9,15 @@
7 9
 from django.template import Context, Template
8 10
 from django.test import TestCase
9 11
 from django.test.client import RequestFactory
  12
+from django.utils import formats
10 13
 
11 14
 from .admin import (ChildAdmin, QuartetAdmin, BandAdmin, ChordsBandAdmin,
12 15
     GroupAdmin, ParentAdmin, DynamicListDisplayChildAdmin,
13 16
     DynamicListDisplayLinksChildAdmin, CustomPaginationAdmin,
14 17
     FilteredChildAdmin, CustomPaginator, site as custom_site,
15 18
     SwallowAdmin)
16  
-from .models import (Child, Parent, Genre, Band, Musician, Group, Quartet,
17  
-    Membership, ChordsMusician, ChordsBand, Invitation, Swallow,
  19
+from .models import (Event, Child, Parent, Genre, Band, Musician, Group,
  20
+    Quartet, Membership, ChordsMusician, ChordsBand, Invitation, Swallow,
18 21
     UnorderedObject, OrderedObject)
19 22
 
20 23
 
@@ -325,6 +328,19 @@ def test_pagination(self):
325 328
         self.assertEqual(cl.paginator.count, 30)
326 329
         self.assertEqual(cl.paginator.page_range, [1, 2, 3])
327 330
 
  331
+    def test_computed_list_display_localization(self):
  332
+        """
  333
+        Regression test for #13196: output of functions should be  localized
  334
+        in the changelist.
  335
+        """
  336
+        User.objects.create_superuser(
  337
+            username='super', email='super@localhost', password='secret')
  338
+        self.client.login(username='super', password='secret')
  339
+        event = Event.objects.create(date=datetime.date.today())
  340
+        response = self.client.get('/admin/admin_changelist/event/')
  341
+        self.assertContains(response, formats.localize(event.date))
  342
+        self.assertNotContains(response, unicode(event.date))
  343
+
328 344
     def test_dynamic_list_display(self):
329 345
         """
330 346
         Regression tests for #14206: dynamic list_display support.
@@ -519,4 +535,4 @@ def check_results_order(ascending=False):
519 535
         OrderedObjectAdmin.ordering = ['-id', 'bool']
520 536
         check_results_order()
521 537
         OrderedObjectAdmin.ordering = ['id', 'bool']
522  
-        check_results_order(ascending=True)
  538
+        check_results_order(ascending=True)

0 notes on commit 905bd7f

Please sign in to comment.
Something went wrong with that request. Please try again.