Code

Ticket #14402: 14402.diff

File 14402.diff, 7.5 KB (added by ramiro, 4 years ago)

Patch, includes tests

Line 
1diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py
2--- a/django/contrib/admin/options.py
3+++ b/django/contrib/admin/options.py
4@@ -172,7 +172,6 @@
5 
6         if db_field.name in self.raw_id_fields:
7             kwargs['widget'] = widgets.ManyToManyRawIdWidget(db_field.rel, using=db)
8-            kwargs['help_text'] = ''
9         elif db_field.name in (list(self.filter_vertical) + list(self.filter_horizontal)):
10             kwargs['widget'] = widgets.FilteredSelectMultiple(db_field.verbose_name, (db_field.name in self.filter_vertical))
11 
12diff --git a/django/contrib/admin/templates/admin/includes/fieldset.html b/django/contrib/admin/templates/admin/includes/fieldset.html
13--- a/django/contrib/admin/templates/admin/includes/fieldset.html
14+++ b/django/contrib/admin/templates/admin/includes/fieldset.html
15@@ -18,8 +18,8 @@
16                             {{ field.field }}
17                         {% endif %}
18                     {% endif %}
19-                    {% if field.field.field.help_text %}
20-                        <p class="help">{{ field.field.field.help_text|safe }}</p>
21+                    {% if field.field.help_text %}
22+                        <p class="help">{{ field.field.help_text|safe }}</p>
23                     {% endif %}
24                 </div>
25             {% endfor %}
26diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py
27--- a/django/db/models/fields/related.py
28+++ b/django/db/models/fields/related.py
29@@ -8,7 +8,7 @@
30 from django.db.models.query import QuerySet
31 from django.db.models.query_utils import QueryWrapper
32 from django.utils.encoding import smart_unicode
33-from django.utils.translation import ugettext_lazy as _, string_concat, ungettext, ugettext
34+from django.utils.translation import ugettext_lazy as _, ungettext, ugettext
35 from django.utils.functional import curry
36 from django.core import exceptions
37 from django import forms
38@@ -1000,9 +1000,6 @@
39 
40         Field.__init__(self, **kwargs)
41 
42-        msg = _('Hold down "Control", or "Command" on a Mac, to select more than one.')
43-        self.help_text = string_concat(self.help_text, ' ', msg)
44-
45     def get_choices_default(self):
46         return Field.get_choices(self, include_blank=False)
47 
48diff --git a/django/forms/models.py b/django/forms/models.py
49--- a/django/forms/models.py
50+++ b/django/forms/models.py
51@@ -7,7 +7,7 @@
52 from django.utils.encoding import smart_unicode, force_unicode
53 from django.utils.datastructures import SortedDict
54 from django.utils.text import get_text_list, capfirst
55-from django.utils.translation import ugettext_lazy as _, ugettext
56+from django.utils.translation import ugettext_lazy as _, ugettext, string_concat
57 
58 from django.core.exceptions import ValidationError, NON_FIELD_ERRORS, \
59                                    FieldError
60@@ -1027,6 +1027,11 @@
61     def __init__(self, queryset, cache_choices=False, required=True,
62                  widget=None, label=None, initial=None,
63                  help_text=None, *args, **kwargs):
64+        msg = _('Hold down "Control", or "Command" on a Mac, to select more than one.')
65+        if help_text is None:
66+            help_text = msg
67+        else:
68+            help_text = string_concat(help_text, ' ', msg)
69         super(ModelMultipleChoiceField, self).__init__(queryset, None,
70             cache_choices, required, widget, label, initial, help_text,
71             *args, **kwargs)
72diff --git a/tests/regressiontests/admin_widgets/models.py b/tests/regressiontests/admin_widgets/models.py
73--- a/tests/regressiontests/admin_widgets/models.py
74+++ b/tests/regressiontests/admin_widgets/models.py
75@@ -4,8 +4,8 @@
76 from django.core.files.storage import default_storage
77 from django.contrib.auth.models import User
78 
79-class MyFileField(models.FileField):
80-    pass
81+class MyFileField(models.FileField):
82+    pass
83 
84 class Member(models.Model):
85     name = models.CharField(max_length=100)
86@@ -31,6 +31,27 @@
87     def __unicode__(self):
88         return self.name
89 
90+class Director(models.Model):
91+    name = models.CharField(max_length=40)
92+
93+    def __unicode__(self):
94+        return self.name
95+
96+class Genre(models.Model):
97+    name = models.CharField(max_length=40)
98+
99+    def __unicode__(self):
100+        return self.name
101+
102+class Orchestra(models.Model):
103+    name = models.CharField(max_length=100)
104+    members = models.ManyToManyField(Member, help_text='This is the orchestra members help_text.')
105+    director = models.ForeignKey(Director)
106+    genres = models.ManyToManyField(Genre, help_text='This is the orchestra genres help_text.')
107+
108+    def __unicode__(self):
109+        return self.name
110+
111 class HiddenInventoryManager(models.Manager):
112     def get_query_set(self):
113         return super(HiddenInventoryManager, self).get_query_set().filter(hidden=False)
114diff --git a/tests/regressiontests/admin_widgets/tests.py b/tests/regressiontests/admin_widgets/tests.py
115--- a/tests/regressiontests/admin_widgets/tests.py
116+++ b/tests/regressiontests/admin_widgets/tests.py
117@@ -163,3 +163,33 @@
118 
119             self.assertContains(response,
120                 'Select a valid choice. That choice is not one of the available choices.')
121+
122+class Ticket14402(DjangoTestCase):
123+    fixtures = ["admin-widgets-users.xml"]
124+    admin_root = '/widget_admin'
125+
126+    def setUp(self):
127+        self.client.login(username="super", password="secret")
128+
129+    def tearDown(self):
130+        self.client.logout()
131+
132+    def test_m2m_has_helptext(self):
133+        """Non raw_id m2m model field help_text shouldn't be affected by the fix for this ticket."""
134+        response = self.client.get('%s/admin_widgets/orchestra/add/' % self.admin_root)
135+        self.assert_("This is the orchestra genres help_text." in response.content)
136+
137+    def test_inline_m2m_has_helptext(self):
138+        """Non raw_id m2m model field help_text shouldn't be affected by the fix for this ticket (inline case)."""
139+        response = self.client.get('%s/admin_widgets/director/add/' % self.admin_root)
140+        self.assert_("This is the orchestra genres help_text." in response.content)
141+
142+    def test_raw_id_m2m_has_helptext(self):
143+        """raw_id m2m model field help_text shouldn't be ignored when displaying its admin widget."""
144+        response = self.client.get('%s/admin_widgets/orchestra/add/' % self.admin_root)
145+        self.assert_("This is the orchestra members help_text." in response.content)
146+
147+    def test_raw_id_inline_m2m_has_helptext(self):
148+        """raw_id m2m model field help_text shouldn't be ignored when displaying the admin widget (inline case)."""
149+        response = self.client.get('%s/admin_widgets/director/add/' % self.admin_root)
150+        self.assert_("This is the orchestra members help_text." in response.content)
151diff --git a/tests/regressiontests/admin_widgets/widgetadmin.py b/tests/regressiontests/admin_widgets/widgetadmin.py
152--- a/tests/regressiontests/admin_widgets/widgetadmin.py
153+++ b/tests/regressiontests/admin_widgets/widgetadmin.py
154@@ -22,9 +22,21 @@
155 class EventAdmin(admin.ModelAdmin):
156     raw_id_fields = ['band']
157 
158+class OrchestraAdmin(admin.ModelAdmin):
159+    raw_id_fields = ['members']
160+
161+class OrchestraInline(admin.StackedInline):
162+    model = models.Orchestra
163+    raw_id_fields = ['members']
164+
165+class DirectorAdmin(admin.ModelAdmin):
166+    inlines = [OrchestraInline]
167+
168 site = WidgetAdmin(name='widget-admin')
169 
170 site.register(models.User)
171 site.register(models.Car, CarAdmin)
172 site.register(models.CarTire, CarTireAdmin)
173 site.register(models.Event, EventAdmin)
174+site.register(models.Orchestra, OrchestraAdmin)
175+site.register(models.Director, DirectorAdmin)