Search code examples
djangodjango-modelsdjango-forms

django clean function in forms.py does not work


Im reading a book called "Web development with Django" which I like a lot. One of the things which gets taught there is that you can change a value coming from the form. I have tested raising errors (validation error from django core) which works fine. But I can't get the "clean_name" function to work on the input. The function is located at the bottom.

I hope someone here knows what im doing wrong :)

from django import forms
from django.forms import widgets
from django.core.exceptions import ValidationError


def validate_regnr(value):
    if value[:3].upper() != value[:3]:
        raise ValidationError(f'{value} is not correct')


class BookingForm(forms.Form):

    SERVICES = (
        ('1', 'Årsservice'),
        ('2', 'Storservice'),
        ('3', 'Bromsservice')
    )

    regnr = forms.CharField(min_length=6, max_length=6, validators=[validate_regnr])
    service = forms.ChoiceField(choices=SERVICES)
    name = forms.CharField(max_length=255)
    tel = forms.CharField(max_length=20)
    email = forms.EmailField()
    datetime = forms.DateTimeField(widget=widgets.DateInput(attrs={'type': 'datetime-local'}))
    kommentar = forms.TextInput()

    def clean_name(self):
        value = self.cleaned_data['name']
        return value.upper()

Solution

  • The order of things is important. First it will run the cleaning and validators defined on the form fields itself. Then it will run the clean_… methods in the form, and finally, it will run the clean method.

    This thus means that the validator in the field runs before it ever gets to the clean_name method. Validators defined on the fields thus can not be satisfied with the clean_… methods, simply because control is never passed to these.

    You can however do the cleaning before validating by subclassing the field:

    class UppercaseCharField(forms.CharField):
        def to_python(self, value):
            val = super().to_python(value)
            if val is not None:
                return val.upper()

    and then use that one:

    class BookingForm(forms.Form):
        # …
        regnr = UpperCharField(
            min_length=6, max_length=6, validators=[validate_regnr]
        )
        # no clean_name …