Commit 905bd7fb authored by Aymeric Augustin's avatar Aymeric Augustin
Browse files

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.
parent 7c27d156
Loading
Loading
Loading
Loading
+6 −7
Original line number Diff line number Diff line
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,
    ORDER_VAR, PAGE_VAR, SEARCH_VAR)
from django.contrib.admin.templatetags.admin_static import static
@@ -184,15 +185,15 @@ def items_for_result(cl, result, form):
                boolean = getattr(attr, 'boolean', False)
                if boolean:
                    allow_tags = True
                    result_repr = _boolean_icon(value)
                else:
                    result_repr = smart_unicode(value)
                result_repr = display_for_value(value, boolean)
                # Strip HTML tags in the resulting text, except if the
                # function has an "allow_tags" attribute set to True.
                if not allow_tags:
                    result_repr = escape(result_repr)
                else:
                    result_repr = mark_safe(result_repr)
                if isinstance(value, (datetime.date, datetime.time)):
                    row_class = ' class="nowrap"'
            else:
                if isinstance(f.rel, models.ManyToOneRel):
                    field_val = getattr(result, f.name)
@@ -202,9 +203,7 @@ def items_for_result(cl, result, form):
                        result_repr = escape(field_val)
                else:
                    result_repr = display_for_field(value, f)
                if isinstance(f, models.DateField)\
                or isinstance(f, models.TimeField)\
                or isinstance(f, models.ForeignKey):
                if isinstance(f, (models.DateField, models.TimeField, models.ForeignKey)):
                    row_class = ' class="nowrap"'
        if force_unicode(result_repr) == '':
            result_repr = mark_safe(' ')
+22 −1
Original line number Diff line number Diff line
import datetime
import decimal

from django.db import models
from django.db.models.sql.constants import LOOKUP_SEP
from django.db.models.deletion import Collector
@@ -323,7 +326,7 @@ def display_for_field(value, field):
        return EMPTY_CHANGELIST_VALUE
    elif isinstance(field, models.DateTimeField):
        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)
    elif isinstance(field, models.DecimalField):
        return formats.number_format(value, field.decimal_places)
@@ -333,6 +336,24 @@ def display_for_field(value, field):
        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):
    pass

+11 −2
Original line number Diff line number Diff line
@@ -3,8 +3,8 @@ from __future__ import absolute_import
from django.contrib import admin
from django.core.paginator import Paginator

from .models import (Child, Parent, Genre, Band, Musician, Group, Quartet,
    Membership, ChordsMusician, ChordsBand, Invitation, Swallow)
from .models import (Event, Child, Parent, Genre, Band, Musician, Group,
    Quartet, Membership, ChordsMusician, ChordsBand, Invitation, Swallow)


site = admin.AdminSite(name="admin")
@@ -15,6 +15,15 @@ class CustomPaginator(Paginator):
            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):
    list_filter = ['child__name']
    search_fields = ['child__name']
+2 −0
Original line number Diff line number Diff line
from django.db import models

class Event(models.Model):
    date = models.DateField()

class Parent(models.Model):
    name = models.CharField(max_length=128)
+19 −3
Original line number Diff line number Diff line
from __future__ import absolute_import

import datetime

from django.contrib import admin
from django.contrib.admin.options import IncorrectLookupParameters
from django.contrib.admin.views.main import ChangeList, SEARCH_VAR, ALL_VAR
@@ -7,14 +9,15 @@ from django.contrib.auth.models import User
from django.template import Context, Template
from django.test import TestCase
from django.test.client import RequestFactory
from django.utils import formats

from .admin import (ChildAdmin, QuartetAdmin, BandAdmin, ChordsBandAdmin,
    GroupAdmin, ParentAdmin, DynamicListDisplayChildAdmin,
    DynamicListDisplayLinksChildAdmin, CustomPaginationAdmin,
    FilteredChildAdmin, CustomPaginator, site as custom_site,
    SwallowAdmin)
from .models import (Child, Parent, Genre, Band, Musician, Group, Quartet,
    Membership, ChordsMusician, ChordsBand, Invitation, Swallow,
from .models import (Event, Child, Parent, Genre, Band, Musician, Group,
    Quartet, Membership, ChordsMusician, ChordsBand, Invitation, Swallow,
    UnorderedObject, OrderedObject)


@@ -325,6 +328,19 @@ class ChangeListTests(TestCase):
        self.assertEqual(cl.paginator.count, 30)
        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):
        """
        Regression tests for #14206: dynamic list_display support.