diff --git a/django/contrib/admin/helpers.py b/django/contrib/admin/helpers.py
index 5f5919d5179..0c0b3a4e345 100644
--- a/django/contrib/admin/helpers.py
+++ b/django/contrib/admin/helpers.py
@@ -279,6 +279,7 @@ class InlineAdminFormSet:
continue
if not self.has_change_permission or field_name in self.readonly_fields:
yield {
+ 'name': field_name,
'label': meta_labels.get(field_name) or label_for_field(field_name, self.opts.model, self.opts),
'widget': {'is_hidden': False},
'required': False,
@@ -290,6 +291,7 @@ class InlineAdminFormSet:
if label is None:
label = label_for_field(field_name, self.opts.model, self.opts)
yield {
+ 'name': field_name,
'label': label,
'widget': form_field.widget,
'required': form_field.required,
diff --git a/django/contrib/admin/templates/admin/edit_inline/tabular.html b/django/contrib/admin/templates/admin/edit_inline/tabular.html
index 2395d1cb6de..531d7b6a215 100644
--- a/django/contrib/admin/templates/admin/edit_inline/tabular.html
+++ b/django/contrib/admin/templates/admin/edit_inline/tabular.html
@@ -12,7 +12,7 @@
{% for field in inline_admin_formset.fields %}
{% if not field.widget.is_hidden %}
- {{ field.label|capfirst }}
+ {{ field.label|capfirst }}
{% if field.help_text %} {% endif %}
{% endif %}
diff --git a/docs/releases/2.2.txt b/docs/releases/2.2.txt
index 01eb2b7d47e..c00cb9cd414 100644
--- a/docs/releases/2.2.txt
+++ b/docs/releases/2.2.txt
@@ -44,7 +44,8 @@ Minor features
:mod:`django.contrib.admin`
~~~~~~~~~~~~~~~~~~~~~~~~~~~
-* ...
+* Added a CSS class to the column headers of
+ :class:`~django.contrib.admin.TabularInline`.
:mod:`django.contrib.admindocs`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/tests/admin_inlines/models.py b/tests/admin_inlines/models.py
index cb1ec39ae50..38b2999f794 100644
--- a/tests/admin_inlines/models.py
+++ b/tests/admin_inlines/models.py
@@ -152,6 +152,7 @@ class Poll(models.Model):
class Question(models.Model):
+ text = models.CharField(max_length=40)
poll = models.ForeignKey(Poll, models.CASCADE)
diff --git a/tests/admin_inlines/tests.py b/tests/admin_inlines/tests.py
index 749b3dd75fd..66cf57dba14 100644
--- a/tests/admin_inlines/tests.py
+++ b/tests/admin_inlines/tests.py
@@ -85,13 +85,27 @@ class TestInline(TestDataMixin, TestCase):
self.assertEqual(response.status_code, 302)
self.assertEqual(len(Fashionista.objects.filter(person__firstname='Imelda')), 1)
+ def test_tabular_inline_column_css_class(self):
+ """
+ Field names are included in the context to output a field-specific
+ CSS class name in the column headers.
+ """
+ response = self.client.get(reverse('admin:admin_inlines_poll_add'))
+ text_field, call_me_field = list(response.context['inline_admin_formset'].fields())
+ # Editable field.
+ self.assertEqual(text_field['name'], 'text')
+ self.assertContains(response, '')
+ # Read-only field.
+ self.assertEqual(call_me_field['name'], 'call_me')
+ self.assertContains(response, ' ')
+
def test_custom_form_tabular_inline_label(self):
"""
A model form with a form field specified (TitleForm.title1) should have
its label rendered in the tabular inline.
"""
response = self.client.get(reverse('admin:admin_inlines_titlecollection_add'))
- self.assertContains(response, ' Title1 ', html=True)
+ self.assertContains(response, 'Title1 ', html=True)
def test_custom_form_tabular_inline_overridden_label(self):
"""
@@ -101,7 +115,7 @@ class TestInline(TestDataMixin, TestCase):
response = self.client.get(reverse('admin:admin_inlines_someparentmodel_add'))
field = list(response.context['inline_admin_formset'].fields())[0]
self.assertEqual(field['label'], 'new label')
- self.assertContains(response, 'New label ', html=True)
+ self.assertContains(response, 'New label ', html=True)
def test_tabular_non_field_errors(self):
"""
@@ -710,7 +724,7 @@ class TestInlinePermissions(TestCase):
html=True
)
# TabularInline
- self.assertContains(response, 'Dummy ', html=True)
+ self.assertContains(response, 'Dummy ', html=True)
self.assertContains(
response,
' Dummy', html=True)
+ self.assertContains(response, 'Dummy ', html=True)
self.assertContains(
response,
'