The validation process begins with form.full_clean () , where you have form._clean_fields () and form._clean_form executed in this order.
Now, if you take a closer look at what form._clean_fields() does, you will probably notice that it only calls field.clean(value, initial) and collects the results in a cleaned_data dict. So, the interesting part is in field.clean , it allows you to see what happens there:
def clean(self, value): """ Validate the given value and return its "cleaned" value as an appropriate Python object. Raise ValidationError for any errors. """ value = self.to_python(value) self.validate(value) self.run_validators(value) return value
First we have a call to_python , followed by validate and ending with run_validators .
So, in terms of ModelChoiceField , when you reach the .validate method, your choice is already an instance of the model, so this validation (from Q2) happens inside to_python .
def to_python(self, value): if value in self.empty_values: return None try: key = self.to_field_name or 'pk' value = self.queryset.get(**{key: value}) except (ValueError, TypeError, self.queryset.model.DoesNotExist): raise ValidationError(self.error_messages['invalid_choice'], code='invalid_choice') return value
Todor
source share