Not sure if I agree with this ticket, but it appears Django won’t be adding support for removing leading / trailing whitespace from form fields. Other frameworks make this super easy. Hopefully the Django folks will put something together for 1.6+.
For now the best approach appears to be creating a base form class which all other forms subclass. The class looks like the following (thanks to Dave Dash and his library):
from django.forms import Form | |
from django.forms import ValidationError | |
from django.forms import FileField | |
# Django 1.6 Version | |
class BaseForm(Form): | |
def _clean_fields(self): | |
for name, field in self.fields.items(): | |
# value_from_datadict() gets the data from the data dictionaries. | |
# Each widget type knows how to retrieve its own data, because some | |
# widgets split data over several HTML fields. | |
value = field.widget.value_from_datadict(self.data, self.files, | |
self.add_prefix(name)) | |
try: | |
if isinstance(field, FileField): | |
initial = self.initial.get(name, field.initial) | |
value = field.clean(value, initial) | |
else: | |
if isinstance(value, basestring): | |
value = field.clean(value.strip()) | |
else: | |
value = field.clean(value) | |
self.cleaned_data[name] = value | |
if hasattr(self, 'clean_%s' % name): | |
value = getattr(self, 'clean_%s' % name)() | |
self.cleaned_data[name] = value | |
except ValidationError, e: | |
self._errors[name] = self.error_class(e.messages) | |
if name in self.cleaned_data: | |
del self.cleaned_data[name] | |
# Django 1.7 Version | |
# (makes use of new add_error API – https://docs.djangoproject.com/en/1.7/ref/forms/api/#django.forms.Form.add_error) | |
class BaseForm(Form): | |
# strip leading or trailing whitespace | |
def _clean_fields(self): | |
for name, field in self.fields.items(): | |
# value_from_datadict() gets the data from the data dictionaries. | |
# Each widget type knows how to retrieve its own data, because some | |
# widgets split data over several HTML fields. | |
value = field.widget.value_from_datadict(self.data, self.files, self.add_prefix(name)) | |
try: | |
if isinstance(field, FileField): | |
initial = self.initial.get(name, field.initial) | |
value = field.clean(value, initial) | |
else: | |
if isinstance(value, basestring): | |
value = field.clean(value.strip()) | |
else: | |
value = field.clean(value) | |
self.cleaned_data[name] = value | |
if hasattr(self, 'clean_%s' % name): | |
value = getattr(self, 'clean_%s' % name)() | |
self.cleaned_data[name] = value | |
except ValidationError as e: | |
self.add_error(name, e) |
I have seen other posts recommending doing the work in the clean method but I find that this runs after any validation. So if you’re doing some regex validation that doesn’t allow leading / trailing spaces for instance, the validation will fail. It’s better to trim the whitespace first, then do the validation to avoid the user having to manually trim the spaces.
This code is also nice since if only whitespace is entered for a required field, the form will not accept it, which is what is expected most of the time.
Will the same code work for a ModelForm?
ie just switching class BaseForm(Form) to BaseModelForm(ModelForm)?
Yes, it sure will.
basemodelform.py
hosted with ❤ by GitHub