diff --git a/django/forms/fields.py b/django/forms/fields.py
index 113a5aa..ea9e6dc 100644
a
|
b
|
class Field(object):
|
54 | 54 | |
55 | 55 | def __init__(self, required=True, widget=None, label=None, initial=None, |
56 | 56 | help_text=None, error_messages=None, show_hidden_initial=False, |
57 | | validators=[], localize=False): |
| 57 | validators=[], localize=False, normalize=None): |
58 | 58 | # required -- Boolean that specifies whether the field is required. |
59 | 59 | # True by default. |
60 | 60 | # widget -- A Widget class, or instance of a Widget class, that should |
… |
… |
class Field(object):
|
82 | 82 | self.help_text = u'' |
83 | 83 | else: |
84 | 84 | self.help_text = smart_unicode(help_text) |
| 85 | self.normalize = normalize |
85 | 86 | widget = widget or self.widget |
86 | 87 | if isinstance(widget, type): |
87 | 88 | widget = widget() |
… |
… |
class Field(object):
|
149 | 150 | Raises ValidationError for any errors. |
150 | 151 | """ |
151 | 152 | value = self.to_python(value) |
| 153 | if self.normalize: |
| 154 | value = self.normalize(value) |
152 | 155 | self.validate(value) |
153 | 156 | self.run_validators(value) |
154 | 157 | return value |
… |
… |
class CharField(Field):
|
186 | 189 | self.validators.append(validators.MinLengthValidator(min_length)) |
187 | 190 | if max_length is not None: |
188 | 191 | self.validators.append(validators.MaxLengthValidator(max_length)) |
| 192 | if self.normalize is None: |
| 193 | self.normalize = lambda value: value.strip() |
189 | 194 | |
190 | 195 | def to_python(self, value): |
191 | 196 | "Returns a Unicode object." |
… |
… |
class DateTimeField(BaseTemporalField):
|
427 | 432 | return datetime.datetime.strptime(value, format) |
428 | 433 | |
429 | 434 | class RegexField(CharField): |
430 | | def __init__(self, regex, max_length=None, min_length=None, error_message=None, *args, **kwargs): |
| 435 | def __init__(self, regex, max_length=None, min_length=None, error_message=None, normalize=False, *args, **kwargs): |
431 | 436 | """ |
432 | 437 | regex can be either a string or a compiled regular expression object. |
433 | 438 | error_message is an optional error message to use, if |
… |
… |
class RegexField(CharField):
|
438 | 443 | error_messages = kwargs.get('error_messages') or {} |
439 | 444 | error_messages['invalid'] = error_message |
440 | 445 | kwargs['error_messages'] = error_messages |
441 | | super(RegexField, self).__init__(max_length, min_length, *args, **kwargs) |
| 446 | super(RegexField, self).__init__(max_length, min_length, normalize=normalize, *args, **kwargs) |
442 | 447 | if isinstance(regex, basestring): |
443 | 448 | regex = re.compile(regex) |
444 | 449 | self.regex = regex |
… |
… |
class EmailField(CharField):
|
450 | 455 | } |
451 | 456 | default_validators = [validators.validate_email] |
452 | 457 | |
453 | | def clean(self, value): |
454 | | value = self.to_python(value).strip() |
455 | | return super(EmailField, self).clean(value) |
456 | | |
457 | 458 | class FileField(Field): |
458 | 459 | widget = ClearableFileInput |
459 | 460 | default_error_messages = { |
diff --git a/docs/ref/forms/fields.txt b/docs/ref/forms/fields.txt
index 66c05e5..c4df0ba 100644
a
|
b
|
as the rendered output.
|
278 | 278 | See the :ref:`format localization <format-localization>` documentation for |
279 | 279 | more information. |
280 | 280 | |
| 281 | ``normalize`` |
| 282 | ~~~~~~~~~~~~ |
| 283 | |
| 284 | .. versionadded:: New in Django development version |
| 285 | |
| 286 | .. attribute:: Field.normalize |
| 287 | |
| 288 | The ``normalize`` argument lets you normalize the input data before it is validated. |
| 289 | A common use-case is stripping leading and trailinig whitespace from CharFields:: |
| 290 | |
| 291 | >>> foo = forms.CharField(normalize=lambda x: x.strip()) |
| 292 | >>> foo.clean(' ') |
| 293 | Traceback (most recent call last): |
| 294 | ... |
| 295 | ValidationError: [u'This field is required.'] |
| 296 | |
| 297 | This behavior for CharField enabled by default. Set `normalize=False`` to disable it. |
| 298 | |
| 299 | >>> foo = forms.CharField(normalize=False) |
| 300 | >>> foo.clean(' ') |
| 301 | u' ' |
| 302 | |
| 303 | |
281 | 304 | .. _built-in fields: |
282 | 305 | |
283 | 306 | Built-in ``Field`` classes |
… |
… |
For each field, we describe the default widget used if you don't specify
|
317 | 340 | |
318 | 341 | * Default widget: ``TextInput`` |
319 | 342 | * Empty value: ``''`` (an empty string) |
320 | | * Normalizes to: A Unicode object. |
| 343 | * Normalizes to: A Unicode object with stripped leading and trailinig whitespace. |
| 344 | Set ``normalize=False`` to disable stripping spaces or set |
| 345 | ``normilize=your_callable_name`` to change normalization behavior. |
321 | 346 | * Validates ``max_length`` or ``min_length``, if they are provided. |
322 | 347 | Otherwise, all inputs are valid. |
323 | 348 | * Error message keys: ``required``, ``max_length``, ``min_length`` |
… |
… |
Takes four optional arguments:
|
481 | 506 | |
482 | 507 | * Default widget: ``TextInput`` |
483 | 508 | * Empty value: ``''`` (an empty string) |
484 | | * Normalizes to: A Unicode object. |
485 | | * Validates that the given value is a valid email address, using a |
| 509 | * Normalizes to: A Unicode object with stripped leading and trailinig whitespace. |
| 510 | Set ``normalize=False`` to disable stripping spaces or set |
| 511 | ``normilize=your_callable_name`` to change normalization behavior. |
| 512 | * Validates that the given value is a valid e-mail address, using a |
486 | 513 | moderately complex regular expression. |
487 | 514 | * Error message keys: ``required``, ``invalid`` |
488 | 515 | |
… |
… |
Takes two optional arguments for validation:
|
618 | 645 | |
619 | 646 | * Default widget: ``TextInput`` |
620 | 647 | * Empty value: ``''`` (an empty string) |
621 | | * Normalizes to: A Unicode object. |
| 648 | * Normalizes to: A Unicode object with stripped leading and trailinig whitespace. |
| 649 | Set ``normalize=False`` to disable stripping spaces or set |
| 650 | ``normilize=your_callable_name`` to change normalization behavior. |
622 | 651 | * Validates that the given value is a valid IPv4 address, using a regular |
623 | 652 | expression. |
624 | 653 | * Error message keys: ``required``, ``invalid`` |
… |
… |
and the error message as the value.
|
746 | 775 | |
747 | 776 | * Default widget: ``TextInput`` |
748 | 777 | * Empty value: ``''`` (an empty string) |
749 | | * Normalizes to: A Unicode object. |
| 778 | * Normalizes to: A Unicode object with stripped leading and trailinig whitespace. |
| 779 | Set ``normalize=False`` to disable stripping spaces or set |
| 780 | ``normilize=your_callable_name`` to change normalization behavior. |
750 | 781 | * Validates that the given value contains only letters, numbers, |
751 | 782 | underscores, and hyphens. |
752 | 783 | * Error messages: ``required``, ``invalid`` |
… |
… |
If no ``input_formats`` argument is provided, the default input formats are::
|
785 | 816 | |
786 | 817 | * Default widget: ``TextInput`` |
787 | 818 | * Empty value: ``''`` (an empty string) |
788 | | * Normalizes to: A Unicode object. |
| 819 | * Normalizes to: A Unicode object with stripped leading and trailinig whitespace. |
| 820 | Set ``normalize=False`` to disable stripping spaces or set |
| 821 | ``normilize=your_callable_name`` to change normalization behavior. |
789 | 822 | * Validates that the given value is a valid URL. |
790 | 823 | * Error message keys: ``required``, ``invalid``, ``invalid_link`` |
791 | 824 | |
diff --git a/tests/regressiontests/forms/tests/fields.py b/tests/regressiontests/forms/tests/fields.py
index 6780413..1dcff0d 100644
a
|
b
|
class FieldsTests(TestCase):
|
131 | 131 | self.assertEqual(f.max_length, None) |
132 | 132 | self.assertEqual(f.min_length, 10) |
133 | 133 | |
| 134 | def test_charfield_normalize(self): |
| 135 | f = CharField() |
| 136 | self.assertRaisesErrorWithMessage(ValidationError, "[u'This field is required.']", f.clean, " \t") |
| 137 | |
| 138 | def test_charfield_normalize_false(self): |
| 139 | f = CharField(normalize=False) |
| 140 | self.assertEqual(" \t", f.clean(" \t")) |
| 141 | |
134 | 142 | # IntegerField ################################################################ |
135 | 143 | |
136 | 144 | def test_integerfield_1(self): |