Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django Form validation overview (quick!)

There is reasonably precise documentation about Django Form validation and I have used it successfully already, so what is my problem?

My problem is remembering this stuff.

The framework involves redundancy, irregularity, some non-obvious names, and of course a lot of behind-the-scenes magic and I do not seem to be able to keep it in my head.

Can somebody help with a description that is faster to scan than the original documentation?

like image 926
Lutz Prechelt Avatar asked Jul 15 '16 10:07

Lutz Prechelt


1 Answers

Assume you have a Form class MyForm with an instance called myform and containing various Fields, in particular a SomeField field called somefield which we use as an example to understand what is going on. SomeField can be from Django or your own code.

The Form validation process

These are the validation steps that Django is going to perform or attempt:

  1. SomeField.to_python(self, value)

    • called for: each Field of myform
    • meaning: converts the string value to its Python target type (e.g. int)
    • takes input from: value
    • returns: value coerced into the proper Python type for SomeField
    • side effects: should have none
    • signals problems by: raise ValidationError
  2. SomeField.validate(self, value)

    • called for: each Field of myform
    • meaning: validates Field locally (just like a validator would)
    • takes input from: value
    • returns: None
    • side effects: should have none
    • signals problems by: raise ValidationError
  3. SomeField.run_validators(self, value)

    • called for: each Field of myform
    • meaning: executes all validators registered for myform.somefield
    • takes input from: value
    • returns: None
    • side effects: should have none
    • signals problems by: raise ValidationError combining all ValidationErrors from the validators into one
  4. SomeField.clean(self, value)

    • called for: each Field of myform
    • meaning: runs to_python, validate, and run_validators
    • takes input from: value
    • returns: the desired ("cleaned") value, usually the result of to_python
    • side effects: Django will insert the return value into myform.cleaned_data
    • signals problems by: not handling any ValidationError raised by the other operations
    • note: do not override.
  5. MyForm.clean_somefield(self)

    • called for: each Field of myform with such a method
    • meaning: validate somefield locally
    • takes input from: self.cleaned_data (no longer just strings now!)
    • returns: the new or unchanged value for somefield
    • side effects: Django will insert the return value into myform.cleaned_data
    • signals problems by: raising ValidationError
    • note: This happens in the same loop as the Field.clean calls.
  6. MyForm.clean(self)

    • called for: myform once
    • meaning: perform any cross-field validation
    • takes input from: self.cleaned_data (no longer just strings now!)
    • returns: either None or a dict that will become cleaned_data
    • side effects: Django will assign a dict return value to myform.cleaned_data
    • signals problems by: calling self.add_error or raising ValidationError. The latter will end up in myform.non_field_errors().
    • note: Beware when accessing cleaned_data, as fields that did not validate will be missing.

Extensions for ModelForms

Validation of a ModelForm has one more step added at the end:

  1. myform.instance.full_clean(): calling validation on the respective model instance (if any).

And a ModelForm's clean method will also have access to the model instance via this instance attribute.

Customizing validation

For making myform validate just like you want, you therefore have different possibilities:

  • At the SomeField class level, you can override SomeField.to_python or SomeField.validate (e.g. by subclassing)
  • For field-level validation at the MyForm class level, you can implement MyForm.clean_somefield or just register a validator: somefield = SomeField(validators=[somevalidator]).
    • That validator can be a standard one from django.core.validators or a custom one.
    • BTW: You can place a validator function into your Form class; simply refrain from adding self as the first parameter.
  • At the MyForm level, you can implement MyForm.clean.

Triggering validation

This validation process can be triggered in various ways:

  • calling myform.full_clean()
  • calling myform.is_valid()
  • accessing myform.errors etc.
like image 60
Lutz Prechelt Avatar answered Oct 12 '22 02:10

Lutz Prechelt