Ticket #9321: 9321.manytomanyfield-helptext.diff
File 9321.manytomanyfield-helptext.diff, 13.3 KB (added by , 14 years ago) |
---|
-
django/db/models/fields/related.py
diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py index ffa5692..3f2b219 100644
a b from django.db.models.query import QuerySet 9 9 from django.db.models.query_utils import QueryWrapper 10 10 from django.db.models.deletion import CASCADE 11 11 from django.utils.encoding import smart_unicode 12 from django.utils.translation import (ugettext_lazy as _, string_concat, 13 ungettext, ugettext) 12 from django.utils.translation import ugettext_lazy as _, ungettext, ugettext 14 13 from django.utils.functional import curry 15 14 from django.core import exceptions 16 15 from django import forms … … class ManyToManyField(RelatedField, Field): 1021 1020 1022 1021 Field.__init__(self, **kwargs) 1023 1022 1024 msg = _('Hold down "Control", or "Command" on a Mac, to select more than one.')1025 self.help_text = string_concat(self.help_text, ' ', msg)1026 1027 1023 def get_choices_default(self): 1028 1024 return Field.get_choices(self, include_blank=False) 1029 1025 -
django/forms/fields.py
diff --git a/django/forms/fields.py b/django/forms/fields.py index a5ea81d..2e35f3d 100644
a b class Field(object): 80 80 label = smart_unicode(label) 81 81 self.required, self.label, self.initial = required, label, initial 82 82 self.show_hidden_initial = show_hidden_initial 83 if help_text is None:84 self.help_text = u''85 else:86 self.help_text = smart_unicode(help_text)87 83 widget = widget or self.widget 88 84 if isinstance(widget, type): 89 85 widget = widget() … … class Field(object): 103 99 104 100 self.widget = widget 105 101 102 if help_text is None: 103 self.help_text = u'' 104 else: 105 self.help_text = smart_unicode(help_text) 106 107 # Allow the widget to append something to the help_text 108 if self.widget.extra_help_text is not None: 109 self.help_text = '%s %s' % (self.help_text, 110 smart_unicode(self.widget.extra_help_text)) 111 106 112 # Increase the creation counter, and save our local copy. 107 113 self.creation_counter = Field.creation_counter 108 114 Field.creation_counter += 1 -
django/forms/widgets.py
diff --git a/django/forms/widgets.py b/django/forms/widgets.py index 03152ea..9acae7b 100644
a b class Widget(object): 142 142 needs_multipart_form = False # Determines does this widget need multipart-encrypted form 143 143 is_localized = False 144 144 is_required = False 145 145 extra_help_text = None # If set, will be appended to the form field's help text. 146 146 147 def __init__(self, attrs=None): 147 148 if attrs is not None: 148 149 self.attrs = attrs.copy() … … class Widget(object): 154 155 obj.attrs = self.attrs.copy() 155 156 memo[id(self)] = obj 156 157 return obj 157 158 158 159 def render(self, name, value, attrs=None): 159 160 """ 160 161 Returns this Widget rendered as HTML, as a Unicode string. … … class NullBooleanSelect(Select): 575 576 return initial != data 576 577 577 578 class SelectMultiple(Select): 579 580 extra_help_text = ugettext_lazy('Hold down "Control", or "Command" ' 581 'on a Mac, to select more than one.') 582 578 583 def render(self, name, value, attrs=None, choices=()): 579 584 if value is None: value = [] 580 585 final_attrs = self.build_attrs(attrs, name=name) … … class RadioSelect(Select): 690 695 id_for_label = classmethod(id_for_label) 691 696 692 697 class CheckboxSelectMultiple(SelectMultiple): 698 699 # Do not show the 'Hold down "Control"' message that appears 700 # for SelectMultiple. 701 extra_help_text = None 702 693 703 def render(self, name, value, attrs=None, choices=()): 694 704 if value is None: value = [] 695 705 has_id = attrs and 'id' in attrs -
docs/ref/forms/widgets.txt
diff --git a/docs/ref/forms/widgets.txt b/docs/ref/forms/widgets.txt index dbdf109..df94475 100644
a b Django will then include the extra attributes in the rendered output:: 258 258 <tr><th>Name:</th><td><input type="text" name="name" class="special"/></td></tr> 259 259 <tr><th>Url:</th><td><input type="text" name="url"/></td></tr> 260 260 <tr><th>Comment:</th><td><input type="text" name="comment" size="40"/></td></tr> 261 262 Adding to the help text 263 ----------------------- 264 265 .. versionadded:: 1.4 266 267 In some cases you might want to add extra information to a form field's help 268 text depending on the widget used. To do so, simply use the widget's 269 :attr:`~extra_help_text` class attribute:: 270 271 class MyCustomSelectMultiple(SelectMultiple): 272 273 extra_help_text = 'You can hold down "Control" to select multiple ones.' -
docs/releases/1.4.txt
diff --git a/docs/releases/1.4.txt b/docs/releases/1.4.txt index 1b8510f..cf9bdee 100644
a b A new helper function, 60 60 ``template.Library`` to ease the creation of template tags that store some 61 61 data in a specified context variable. 62 62 63 Widget.extra_help_text 64 ~~~~~~~~~~~~~~~~~~~~~~ 65 66 The :attr:`~extra_help_text` class attribute was added to 67 `:class:~forms.widgets.Widget` to allow a widget to add extra information to 68 the help text provided by a form field or a model field. This introduced a 69 backwards incompatibility if you are defining custom ``SelectMultiple`` 70 widgets. See `the notes on backwards incompatible changes`_ below to address 71 this change. 72 73 .. _the notes on backwards incompatible changes: backwards-incompatible-changes-1.4_ 74 63 75 .. _backwards-incompatible-changes-1.4: 64 76 65 77 Backwards incompatible changes in 1.4 … … you should add the following lines in your settings file:: 214 226 215 227 Don't forget to escape characters that have a special meaning in a regular 216 228 expression. 229 230 Custom ``SelectMultiple`` widgets and ``extra_help_text`` 231 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 232 233 The introduction of a new :attr:`~Widget.extra_help_text` class attribute in 234 this release was originally prompted by the fixing of a bug where the 235 '`Hold down "Control", or "Command" on a Mac, to select more than one.`' 236 message would systematically be appended to any ``ManyToManyField``'s help 237 text, even if that field were configured to use a different widget than 238 ``SelectMultiple``. Thus, the responsibility for appending that message was 239 transfered from ``ManyToManyField`` to ``SelectMultiple`` via the new 240 ``extra_help_text`` attribute:: 241 242 class SelectMultiple(Select): 243 244 extra_help_text = ugettext_lazy('Hold down "Control", or "Command" ' 245 'on a Mac, to select more than one.') 246 247 This means that if you are defining a widget that inherits from 248 ``SelectMultiple``, then that extra message will now systematically be appended 249 to the help text, which may not make sense in the context of use for your 250 widget. To cancel this new behaviour, simply override the ``extra_help_text`` 251 class attribute of your widget like it is done, for example, by the 252 `:class:~forms.widgets.CheckboxSelectMultiple` class:: 253 254 class CheckboxSelectMultiple(SelectMultiple): 255 256 extra_help_text = None -
tests/regressiontests/forms/tests/forms.py
diff --git a/tests/regressiontests/forms/tests/forms.py b/tests/regressiontests/forms/tests/forms.py index 91a7472..5b2b7b4 100644
a b class FormsTestCase(TestCase): 1092 1092 <option value="f" selected="selected">foo</option> 1093 1093 <option value="b" selected="selected">bar</option> 1094 1094 <option value="w">whiz</option> 1095 </select> </li>""")1095 </select> <span class="helptext"> Hold down "Control", or "Command" on a Mac, to select more than one.</span></li>""") 1096 1096 1097 1097 # The 'initial' parameter is meaningless if you pass data. 1098 1098 p = UserRegistration({}, initial={'username': initial_django, 'options': initial_options}, auto_id=False) … … class FormsTestCase(TestCase): 1102 1102 <option value="f">foo</option> 1103 1103 <option value="b">bar</option> 1104 1104 <option value="w">whiz</option> 1105 </select> </li>""")1105 </select> <span class="helptext"> Hold down "Control", or "Command" on a Mac, to select more than one.</span></li>""") 1106 1106 p = UserRegistration({'username': u''}, initial={'username': initial_django}, auto_id=False) 1107 1107 self.assertEqual(p.as_ul(), """<li><ul class="errorlist"><li>This field is required.</li></ul>Username: <input type="text" name="username" maxlength="10" /></li> 1108 1108 <li><ul class="errorlist"><li>This field is required.</li></ul>Password: <input type="password" name="password" /></li> … … class FormsTestCase(TestCase): 1110 1110 <option value="f">foo</option> 1111 1111 <option value="b">bar</option> 1112 1112 <option value="w">whiz</option> 1113 </select> </li>""")1113 </select> <span class="helptext"> Hold down "Control", or "Command" on a Mac, to select more than one.</span></li>""") 1114 1114 p = UserRegistration({'username': u'foo', 'options':['f','b']}, initial={'username': initial_django}, auto_id=False) 1115 1115 self.assertEqual(p.as_ul(), """<li>Username: <input type="text" name="username" value="foo" maxlength="10" /></li> 1116 1116 <li><ul class="errorlist"><li>This field is required.</li></ul>Password: <input type="password" name="password" /></li> … … class FormsTestCase(TestCase): 1118 1118 <option value="f" selected="selected">foo</option> 1119 1119 <option value="b" selected="selected">bar</option> 1120 1120 <option value="w">whiz</option> 1121 </select> </li>""")1121 </select> <span class="helptext"> Hold down "Control", or "Command" on a Mac, to select more than one.</span></li>""") 1122 1122 1123 1123 # A callable 'initial' value is *not* used as a fallback if data is not provided. 1124 1124 # In this example, we don't provide a value for 'username', and the form raises a … … class FormsTestCase(TestCase): 1141 1141 <option value="f">foo</option> 1142 1142 <option value="b" selected="selected">bar</option> 1143 1143 <option value="w" selected="selected">whiz</option> 1144 </select> </li>""")1144 </select> <span class="helptext"> Hold down "Control", or "Command" on a Mac, to select more than one.</span></li>""") 1145 1145 p = UserRegistration(initial={'username': initial_stephane, 'options': initial_options}, auto_id=False) 1146 1146 self.assertEqual(p.as_ul(), """<li>Username: <input type="text" name="username" value="stephane" maxlength="10" /></li> 1147 1147 <li>Password: <input type="password" name="password" /></li> … … class FormsTestCase(TestCase): 1149 1149 <option value="f" selected="selected">foo</option> 1150 1150 <option value="b" selected="selected">bar</option> 1151 1151 <option value="w">whiz</option> 1152 </select> </li>""")1152 </select> <span class="helptext"> Hold down "Control", or "Command" on a Mac, to select more than one.</span></li>""") 1153 1153 1154 1154 def test_boundfield_values(self): 1155 1155 # It's possible to get to the value which would be used for rendering -
tests/regressiontests/model_forms_regress/tests.py
diff --git a/tests/regressiontests/model_forms_regress/tests.py b/tests/regressiontests/model_forms_regress/tests.py index f536001..bdd3b5b 100644
a b from datetime import date 3 3 from django import forms 4 4 from django.core.exceptions import FieldError, ValidationError 5 5 from django.core.files.uploadedfile import SimpleUploadedFile 6 from django.forms.widgets import CheckboxSelectMultiple 6 7 from django.forms.models import (modelform_factory, ModelChoiceField, 7 8 fields_for_model, construct_instance) 8 9 from django.utils import unittest … … class ManyToManyCallableInitialTests(TestCase): 140 141 </select> <span class="helptext"> Hold down "Control", or "Command" on a Mac, to select more than one.</span></li>""" 141 142 % (book1.pk, book2.pk, book3.pk)) 142 143 144 class ManyToManyHelpTextTests(TestCase): 145 146 def test_dont_display_hold_down_command_help_text(self): 147 """ 148 Ensures that the 'Hold down "Control"' message is not systematically displayed for 149 a M2M field if it does not use the default SelectMultiple widget. 150 Refs #9321. 151 """ 152 # Override the widget 153 def formfield_for_dbfield(db_field, **kwargs): 154 if db_field.name == 'publications': 155 kwargs['widget'] = CheckboxSelectMultiple 156 return db_field.formfield(**kwargs) 157 158 # Set up some Publications to use as data 159 book1 = Publication.objects.create(title="First Book", date_published=date(2007,1,1)) 160 book2 = Publication.objects.create(title="Second Book", date_published=date(2008,1,1)) 161 162 # Create a ModelForm, instantiate it, and check that the output is as expected 163 ModelForm = modelform_factory(Article, formfield_callback=formfield_for_dbfield) 164 form = ModelForm() 165 self.assertEqual(unicode(form['publications'].help_text), u'') 166 143 167 class CFFForm(forms.ModelForm): 144 168 class Meta: 145 169 model = CustomFF -
tests/regressiontests/model_formsets_regress/tests.py
diff --git a/tests/regressiontests/model_formsets_regress/tests.py b/tests/regressiontests/model_formsets_regress/tests.py index e6c2633..bf86a85 100644
a b class FormsetTests(TestCase): 228 228 self.assertTrue(isinstance(form.errors, ErrorDict)) 229 229 self.assertTrue(isinstance(form.non_field_errors(), ErrorList)) 230 230 231 class CustomWidget(forms. CharField):231 class CustomWidget(forms.TextInput): 232 232 pass 233 233 234 234