Code

Ticket #13510: 13510_tabular_inline_errors_colspan.diff

File 13510_tabular_inline_errors_colspan.diff, 5.0 KB (added by julien, 3 years ago)
Line 
1diff --git a/django/contrib/admin/templates/admin/edit_inline/tabular.html b/django/contrib/admin/templates/admin/edit_inline/tabular.html
2index a5b1a89..7f6c34a 100644
3--- a/django/contrib/admin/templates/admin/edit_inline/tabular.html
4+++ b/django/contrib/admin/templates/admin/edit_inline/tabular.html
5@@ -1,4 +1,4 @@
6-{% load i18n adminmedia %}
7+{% load i18n adminmedia admin_modify %}
8 <div class="inline-group" id="{{ inline_admin_formset.formset.prefix }}-group">
9   <div class="tabular inline-related {% if forloop.last %}last-related{% endif %}">
10 {{ inline_admin_formset.formset.management_form }}
11@@ -18,7 +18,7 @@
12      <tbody>
13      {% for inline_admin_form in inline_admin_formset %}
14         {% if inline_admin_form.form.non_field_errors %}
15-        <tr><td colspan="{{ inline_admin_form.field_count }}">{{ inline_admin_form.form.non_field_errors }}</td></tr>
16+        <tr><td colspan="{{ inline_admin_form|cell_count }}">{{ inline_admin_form.form.non_field_errors }}</td></tr>
17         {% endif %}
18         <tr class="{% cycle "row1" "row2" %} {% if inline_admin_form.original or inline_admin_form.show_url %}has_original{% endif %}{% if forloop.last %} empty-form{% endif %}"
19              id="{{ inline_admin_formset.formset.prefix }}-{% if not forloop.last %}{{ forloop.counter0 }}{% else %}empty{% endif %}">
20diff --git a/django/contrib/admin/templatetags/admin_modify.py b/django/contrib/admin/templatetags/admin_modify.py
21index fe88043..5c00de3 100644
22--- a/django/contrib/admin/templatetags/admin_modify.py
23+++ b/django/contrib/admin/templatetags/admin_modify.py
24@@ -40,3 +40,17 @@ def submit_row(context):
25         'show_save': True
26     }
27 submit_row = register.inclusion_tag('admin/submit_line.html', takes_context=True)(submit_row)
28+
29+def cell_count(inline_admin_form):
30+    """ Returns the number of cells used in a tabular inline """
31+    count = 1 # Hidden cell with hidden 'id' field
32+    for fieldset in inline_admin_form:
33+        # Loop through all the fields (one per cell)
34+        for line in fieldset:
35+            for field in line:
36+                count += 1
37+    if inline_admin_form.formset.can_delete:
38+        # Delete checkbox
39+        count += 1
40+    return count
41+cell_count = register.filter(cell_count)
42diff --git a/tests/regressiontests/admin_inlines/models.py b/tests/regressiontests/admin_inlines/models.py
43index 4e5c4e3..772152a 100644
44--- a/tests/regressiontests/admin_inlines/models.py
45+++ b/tests/regressiontests/admin_inlines/models.py
46@@ -6,6 +6,7 @@ from django.db import models
47 from django.contrib import admin
48 from django.contrib.contenttypes.models import ContentType
49 from django.contrib.contenttypes import generic
50+from django import forms
51 
52 class Parent(models.Model):
53     name = models.CharField(max_length=50)
54@@ -123,3 +124,31 @@ class InlineWeakness(admin.TabularInline):
55     extra = 1
56 
57 admin.site.register(Fashionista, inlines=[InlineWeakness])
58+
59+
60+# Models for #13510 --------------------
61+
62+class TitleCollection(models.Model):
63+    pass
64+   
65+class Title(models.Model):
66+    collection = models.ForeignKey(TitleCollection, blank=True, null=True)
67+    title1 = models.CharField(max_length=100)
68+    title2 = models.CharField(max_length=100)
69+   
70+class TitleForm(forms.ModelForm):
71+   
72+    def clean(self):
73+        cleaned_data = self.cleaned_data
74+        title1 = cleaned_data.get("title1")
75+        title2 = cleaned_data.get("title2")
76+        if title1 != title2:
77+            raise forms.ValidationError("The two titles must be the same")
78+        return cleaned_data
79+
80+class TitleInline(admin.TabularInline):
81+    model = Title
82+    form = TitleForm
83+    extra = 1
84+
85+admin.site.register(TitleCollection, inlines=[TitleInline])
86diff --git a/tests/regressiontests/admin_inlines/tests.py b/tests/regressiontests/admin_inlines/tests.py
87index b10474d..69ff96c 100644
88--- a/tests/regressiontests/admin_inlines/tests.py
89+++ b/tests/regressiontests/admin_inlines/tests.py
90@@ -67,6 +67,23 @@ class TestInline(TestCase):
91         self.assertEqual(response.status_code, 302)
92         self.assertEqual(len(Fashionista.objects.filter(person__firstname='Imelda')), 1)
93 
94+    def test_tabular_non_field_errors(self):
95+        """ Ensure that non_field_errors are displayed correctly, including
96+            the right value for colspan.
97+            Refs #13510.
98+        """
99+        data = {
100+            'title_set-TOTAL_FORMS': 1,
101+            'title_set-INITIAL_FORMS': 0,
102+            'title_set-MAX_NUM_FORMS': 0,
103+            '_save': u'Save',
104+            'title_set-0-title1': 'a title',
105+            'title_set-0-title2': 'a different title',
106+        }
107+        response = self.client.post('/test_admin/admin/admin_inlines/titlecollection/add/', data)
108+        # Here colspan is "4": two fields (title1 and title2), one hidden field and the delete checkbock.
109+        self.assertContains(response, '<tr><td colspan="4"><ul class="errorlist"><li>The two titles must be the same</li></ul></td></tr>')
110+       
111 class TestInlineMedia(TestCase):
112     fixtures = ['admin-views-users.xml']
113