diff --git a/django/forms/fields.py b/django/forms/fields.py index 1d609d2455..afbefb64c0 100644 --- a/django/forms/fields.py +++ b/django/forms/fields.py @@ -210,8 +210,10 @@ class Field(six.with_metaclass(RenameFieldMethods, object)): class CharField(Field): - def __init__(self, max_length=None, min_length=None, *args, **kwargs): - self.max_length, self.min_length = max_length, min_length + def __init__(self, max_length=None, min_length=None, strip=True, *args, **kwargs): + self.max_length = max_length + self.min_length = min_length + self.strip = strip super(CharField, self).__init__(*args, **kwargs) if min_length is not None: self.validators.append(validators.MinLengthValidator(int(min_length))) @@ -222,7 +224,10 @@ class CharField(Field): "Returns a Unicode object." if value in self.empty_values: return '' - return smart_text(value) + value = force_text(value) + if self.strip: + value = value.strip() + return value def widget_attrs(self, widget): attrs = super(CharField, self).widget_attrs(widget) @@ -539,6 +544,7 @@ class RegexField(CharField): error_message is an optional error message to use, if 'Enter a valid value' is too generic for you. """ + kwargs.setdefault('strip', False) # error_message is just kept for backwards compatibility: if error_message is not None: warnings.warn( @@ -1231,10 +1237,6 @@ class GenericIPAddressField(CharField): class SlugField(CharField): default_validators = [validators.validate_slug] - def clean(self, value): - value = self.to_python(value).strip() - return super(SlugField, self).clean(value) - class UUIDField(CharField): default_error_messages = { diff --git a/docs/ref/forms/fields.txt b/docs/ref/forms/fields.txt index dbade5c5d9..c4a40021d0 100644 --- a/docs/ref/forms/fields.txt +++ b/docs/ref/forms/fields.txt @@ -361,7 +361,7 @@ For each field, we describe the default widget used if you don't specify Otherwise, all inputs are valid. * Error message keys: ``required``, ``max_length``, ``min_length`` - Has two optional arguments for validation: + Has three optional arguments for validation: .. attribute:: max_length .. attribute:: min_length @@ -369,6 +369,13 @@ For each field, we describe the default widget used if you don't specify If provided, these arguments ensure that the string is at most or at least the given length. + .. attribute:: strip + + .. versionadded:: 1.9 + + If ``True`` (default), the value will be stripped of leading and + trailing whitespace. + ``ChoiceField`` ~~~~~~~~~~~~~~~ @@ -824,8 +831,15 @@ For each field, we describe the default widget used if you don't specify A regular expression specified either as a string or a compiled regular expression object. - Also takes ``max_length`` and ``min_length``, which work just as they do for - ``CharField``. + Also takes ``max_length``, ``min_length``, and ``strip``, which work just + as they do for ``CharField``. + + .. attribute:: strip + + .. versionadded:: 1.9 + + Defaults to ``False``. If enabled, stripping will be applied before the + regex validation. .. deprecated:: 1.8 diff --git a/docs/releases/1.9.txt b/docs/releases/1.9.txt index 48c850f9a7..b1a799f8bf 100644 --- a/docs/releases/1.9.txt +++ b/docs/releases/1.9.txt @@ -302,6 +302,11 @@ Forms * You can now :ref:`specify keyword arguments ` that you want to pass to the constructor of forms in a formset. +* :class:`~django.forms.CharField` now accepts a + :attr:`~django.forms.CharField.strip` argument to strip input data of leading + and trailing whitespace. As this defaults to ``True`` this is different + behavior from previous releases. + Generic Views ^^^^^^^^^^^^^ @@ -552,7 +557,7 @@ Database backend API SQLite backend to add time lookups (hour, minute, second) to :class:`~django.db.models.TimeField`, and may be needed by third-party database backends. - + * The ``DatabaseOperations.datetime_cast_sql()`` method (not to be confused with ``DatabaseOperations.datetime_cast_date_sql()`` mentioned above) has been removed. This method served to format dates on Oracle long @@ -822,6 +827,10 @@ Miscellaneous be serialized contains any control characters not allowed in the XML 1.0 standard, the serialization will fail with a :exc:`ValueError`. +* :class:`~django.forms.CharField` now strips input of leading and trailing + whitespace by default. This can be disabled by setting the new + :attr:`~django.forms.CharField.strip` argument to ``False``. + .. _deprecated-features-1.9: Features deprecated in 1.9 diff --git a/tests/forms_tests/tests/test_fields.py b/tests/forms_tests/tests/test_fields.py index 41ed7b5f45..bfc88d7162 100644 --- a/tests/forms_tests/tests/test_fields.py +++ b/tests/forms_tests/tests/test_fields.py @@ -164,6 +164,18 @@ class FieldsTests(SimpleTestCase): self.assertEqual(f.widget_attrs(PasswordInput()), {'maxlength': '10'}) self.assertEqual(f.widget_attrs(Textarea()), {'maxlength': '10'}) + def test_charfield_strip(self): + """ + Ensure that values have whitespace stripped and that strip=False works. + """ + f = CharField() + self.assertEqual(f.clean(' 1'), '1') + self.assertEqual(f.clean('1 '), '1') + + f = CharField(strip=False) + self.assertEqual(f.clean(' 1'), ' 1') + self.assertEqual(f.clean('1 '), '1 ') + # IntegerField ################################################################ def test_integerfield_1(self):