Fixed #11195 -- Added CSS classes to the changelist cells to allow style customizations. Thanks to akaihola, Ramiro Morales and vdboor for their work on the patch.
This commit is contained in:
parent
47c755327b
commit
4e0ff35146
|
@ -180,7 +180,7 @@ def items_for_result(cl, result, form):
|
||||||
first = True
|
first = True
|
||||||
pk = cl.lookup_opts.pk.attname
|
pk = cl.lookup_opts.pk.attname
|
||||||
for field_name in cl.list_display:
|
for field_name in cl.list_display:
|
||||||
row_class = ''
|
row_classes = ['field-%s' % field_name]
|
||||||
try:
|
try:
|
||||||
f, attr, value = lookup_field(field_name, result, cl.model_admin)
|
f, attr, value = lookup_field(field_name, result, cl.model_admin)
|
||||||
except ObjectDoesNotExist:
|
except ObjectDoesNotExist:
|
||||||
|
@ -188,7 +188,7 @@ def items_for_result(cl, result, form):
|
||||||
else:
|
else:
|
||||||
if f is None:
|
if f is None:
|
||||||
if field_name == 'action_checkbox':
|
if field_name == 'action_checkbox':
|
||||||
row_class = mark_safe(' class="action-checkbox"')
|
row_classes = ['action-checkbox']
|
||||||
allow_tags = getattr(attr, 'allow_tags', False)
|
allow_tags = getattr(attr, 'allow_tags', False)
|
||||||
boolean = getattr(attr, 'boolean', False)
|
boolean = getattr(attr, 'boolean', False)
|
||||||
if boolean:
|
if boolean:
|
||||||
|
@ -199,7 +199,7 @@ def items_for_result(cl, result, form):
|
||||||
if allow_tags:
|
if allow_tags:
|
||||||
result_repr = mark_safe(result_repr)
|
result_repr = mark_safe(result_repr)
|
||||||
if isinstance(value, (datetime.date, datetime.time)):
|
if isinstance(value, (datetime.date, datetime.time)):
|
||||||
row_class = mark_safe(' class="nowrap"')
|
row_classes.append('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)
|
||||||
|
@ -210,9 +210,10 @@ def items_for_result(cl, result, form):
|
||||||
else:
|
else:
|
||||||
result_repr = display_for_field(value, f)
|
result_repr = display_for_field(value, f)
|
||||||
if isinstance(f, (models.DateField, models.TimeField, models.ForeignKey)):
|
if isinstance(f, (models.DateField, models.TimeField, models.ForeignKey)):
|
||||||
row_class = mark_safe(' class="nowrap"')
|
row_classes.append('nowrap')
|
||||||
if force_text(result_repr) == '':
|
if force_text(result_repr) == '':
|
||||||
result_repr = mark_safe(' ')
|
result_repr = mark_safe(' ')
|
||||||
|
row_class = mark_safe(' class="%s"' % ' '.join(row_classes))
|
||||||
# If list_display_links not defined, add the link tag to the first field
|
# If list_display_links not defined, add the link tag to the first field
|
||||||
if (first and not cl.list_display_links) or field_name in cl.list_display_links:
|
if (first and not cl.list_display_links) or field_name in cl.list_display_links:
|
||||||
table_tag = {True:'th', False:'td'}[first]
|
table_tag = {True:'th', False:'td'}[first]
|
||||||
|
|
|
@ -82,6 +82,9 @@ Minor features
|
||||||
* Buttons in :mod:`django.contrib.admin` now use the ``border-radius`` CSS
|
* Buttons in :mod:`django.contrib.admin` now use the ``border-radius`` CSS
|
||||||
property for rounded corners rather than GIF background images.
|
property for rounded corners rather than GIF background images.
|
||||||
|
|
||||||
|
* The admin changelist cells now have a ``field-<field_name>`` class in the
|
||||||
|
HTML to enable style customizations.
|
||||||
|
|
||||||
Backwards incompatible changes in 1.7
|
Backwards incompatible changes in 1.7
|
||||||
=====================================
|
=====================================
|
||||||
|
|
||||||
|
|
|
@ -91,7 +91,7 @@ class ChangeListTests(TestCase):
|
||||||
context = Context({'cl': cl})
|
context = Context({'cl': cl})
|
||||||
table_output = template.render(context)
|
table_output = template.render(context)
|
||||||
link = reverse('admin:admin_changelist_child_change', args=(new_child.id,))
|
link = reverse('admin:admin_changelist_child_change', args=(new_child.id,))
|
||||||
row_html = '<tbody><tr class="row1"><th><a href="%s">name</a></th><td class="nowrap">(None)</td></tr></tbody>' % link
|
row_html = '<tbody><tr class="row1"><th class="field-name"><a href="%s">name</a></th><td class="field-parent nowrap">(None)</td></tr></tbody>' % link
|
||||||
self.assertFalse(table_output.find(row_html) == -1,
|
self.assertFalse(table_output.find(row_html) == -1,
|
||||||
'Failed to find expected row element: %s' % table_output)
|
'Failed to find expected row element: %s' % table_output)
|
||||||
|
|
||||||
|
@ -114,7 +114,7 @@ class ChangeListTests(TestCase):
|
||||||
context = Context({'cl': cl})
|
context = Context({'cl': cl})
|
||||||
table_output = template.render(context)
|
table_output = template.render(context)
|
||||||
link = reverse('admin:admin_changelist_child_change', args=(new_child.id,))
|
link = reverse('admin:admin_changelist_child_change', args=(new_child.id,))
|
||||||
row_html = '<tbody><tr class="row1"><th><a href="%s">name</a></th><td class="nowrap">Parent object</td></tr></tbody>' % link
|
row_html = '<tbody><tr class="row1"><th class="field-name"><a href="%s">name</a></th><td class="field-parent nowrap">Parent object</td></tr></tbody>' % link
|
||||||
self.assertFalse(table_output.find(row_html) == -1,
|
self.assertFalse(table_output.find(row_html) == -1,
|
||||||
'Failed to find expected row element: %s' % table_output)
|
'Failed to find expected row element: %s' % table_output)
|
||||||
|
|
||||||
|
@ -150,7 +150,7 @@ class ChangeListTests(TestCase):
|
||||||
|
|
||||||
# make sure that list editable fields are rendered in divs correctly
|
# make sure that list editable fields are rendered in divs correctly
|
||||||
editable_name_field = '<input name="form-0-name" value="name" class="vTextField" maxlength="30" type="text" id="id_form-0-name" />'
|
editable_name_field = '<input name="form-0-name" value="name" class="vTextField" maxlength="30" type="text" id="id_form-0-name" />'
|
||||||
self.assertInHTML('<td>%s</td>' % editable_name_field, table_output, msg_prefix='Failed to find "name" list_editable field')
|
self.assertInHTML('<td class="field-name">%s</td>' % editable_name_field, table_output, msg_prefix='Failed to find "name" list_editable field')
|
||||||
|
|
||||||
def test_result_list_editable(self):
|
def test_result_list_editable(self):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -1496,7 +1496,7 @@ class AdminViewStringPrimaryKeyTest(TestCase):
|
||||||
response = self.client.get(prefix)
|
response = self.client.get(prefix)
|
||||||
# this URL now comes through reverse(), thus url quoting and iri_to_uri encoding
|
# this URL now comes through reverse(), thus url quoting and iri_to_uri encoding
|
||||||
pk_final_url = escape(iri_to_uri(urlquote(quote(self.pk))))
|
pk_final_url = escape(iri_to_uri(urlquote(quote(self.pk))))
|
||||||
should_contain = """<th><a href="%s%s/">%s</a></th>""" % (prefix, pk_final_url, escape(self.pk))
|
should_contain = """<th class="field-__str__"><a href="%s%s/">%s</a></th>""" % (prefix, pk_final_url, escape(self.pk))
|
||||||
self.assertContains(response, should_contain)
|
self.assertContains(response, should_contain)
|
||||||
|
|
||||||
def test_recentactions_link(self):
|
def test_recentactions_link(self):
|
||||||
|
@ -2151,8 +2151,8 @@ class AdminViewListEditable(TestCase):
|
||||||
self.assertContains(response, 'id="id_form-0-id"', 1) # Only one hidden field, in a separate place than the table.
|
self.assertContains(response, 'id="id_form-0-id"', 1) # Only one hidden field, in a separate place than the table.
|
||||||
self.assertContains(response, 'id="id_form-1-id"', 1)
|
self.assertContains(response, 'id="id_form-1-id"', 1)
|
||||||
self.assertContains(response, '<div class="hiddenfields">\n<input type="hidden" name="form-0-id" value="%d" id="id_form-0-id" /><input type="hidden" name="form-1-id" value="%d" id="id_form-1-id" />\n</div>' % (story2.id, story1.id), html=True)
|
self.assertContains(response, '<div class="hiddenfields">\n<input type="hidden" name="form-0-id" value="%d" id="id_form-0-id" /><input type="hidden" name="form-1-id" value="%d" id="id_form-1-id" />\n</div>' % (story2.id, story1.id), html=True)
|
||||||
self.assertContains(response, '<td>%d</td>' % story1.id, 1)
|
self.assertContains(response, '<td class="field-id">%d</td>' % story1.id, 1)
|
||||||
self.assertContains(response, '<td>%d</td>' % story2.id, 1)
|
self.assertContains(response, '<td class="field-id">%d</td>' % story2.id, 1)
|
||||||
|
|
||||||
def test_pk_hidden_fields_with_list_display_links(self):
|
def test_pk_hidden_fields_with_list_display_links(self):
|
||||||
""" Similarly as test_pk_hidden_fields, but when the hidden pk fields are
|
""" Similarly as test_pk_hidden_fields, but when the hidden pk fields are
|
||||||
|
@ -2167,8 +2167,8 @@ class AdminViewListEditable(TestCase):
|
||||||
self.assertContains(response, 'id="id_form-0-id"', 1) # Only one hidden field, in a separate place than the table.
|
self.assertContains(response, 'id="id_form-0-id"', 1) # Only one hidden field, in a separate place than the table.
|
||||||
self.assertContains(response, 'id="id_form-1-id"', 1)
|
self.assertContains(response, 'id="id_form-1-id"', 1)
|
||||||
self.assertContains(response, '<div class="hiddenfields">\n<input type="hidden" name="form-0-id" value="%d" id="id_form-0-id" /><input type="hidden" name="form-1-id" value="%d" id="id_form-1-id" />\n</div>' % (story2.id, story1.id), html=True)
|
self.assertContains(response, '<div class="hiddenfields">\n<input type="hidden" name="form-0-id" value="%d" id="id_form-0-id" /><input type="hidden" name="form-1-id" value="%d" id="id_form-1-id" />\n</div>' % (story2.id, story1.id), html=True)
|
||||||
self.assertContains(response, '<th><a href="%s">%d</a></th>' % (link1, story1.id), 1)
|
self.assertContains(response, '<th class="field-id"><a href="%s">%d</a></th>' % (link1, story1.id), 1)
|
||||||
self.assertContains(response, '<th><a href="%s">%d</a></th>' % (link2, story2.id), 1)
|
self.assertContains(response, '<th class="field-id"><a href="%s">%d</a></th>' % (link2, story2.id), 1)
|
||||||
|
|
||||||
|
|
||||||
@override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',))
|
@override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',))
|
||||||
|
@ -3877,6 +3877,22 @@ class CSSTest(TestCase):
|
||||||
self.assertContains(response,
|
self.assertContains(response,
|
||||||
'<body class="app-admin_views model-section ')
|
'<body class="app-admin_views model-section ')
|
||||||
|
|
||||||
|
def test_changelist_field_classes(self):
|
||||||
|
"""
|
||||||
|
Cells of the change list table should contain the field name in their class attribute
|
||||||
|
Refs #11195.
|
||||||
|
"""
|
||||||
|
Podcast.objects.create(name="Django Dose",
|
||||||
|
release_date=datetime.date.today())
|
||||||
|
response = self.client.get('/test_admin/admin/admin_views/podcast/')
|
||||||
|
self.assertContains(
|
||||||
|
response, '<th class="field-name">')
|
||||||
|
self.assertContains(
|
||||||
|
response, '<td class="field-release_date nowrap">')
|
||||||
|
self.assertContains(
|
||||||
|
response, '<td class="action-checkbox">')
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import docutils
|
import docutils
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
|
Loading…
Reference in New Issue