I see that forms.ChoiceField
is using this code to validate the value:
def validate(self, value):
"""
Validates that the input is in self.choices.
"""
super(ChoiceField, self).validate(value)
if value and not self.valid_value(value):
raise ValidationError(
self.error_messages['invalid_choice'],
code='invalid_choice',
params={'value': value},
)
def valid_value(self, value):
"Check to see if the provided value is a valid choice"
text_value = force_text(value)
for k, v in self.choices:
if isinstance(v, (list, tuple)):
# This is an optgroup, so look inside the group for options
for k2, v2 in v:
if value == k2 or text_value == force_text(k2):
return True
else:
if value == k or text_value == force_text(k):
return True
return False
and forms.models.ModelChoiceField
this code:
def validate(self, value):
return Field.validate(self, value)
Q1. Why Django uses validation to check if the selected value (from dropdown) is indeed in the choice list for forms.ChoiceField
?
Q2. When Django uses the validation from Q1, to check if the value is indeed in the choice list, why does not also check if the selected value is in the model records for forms.models.ModelChoiceField
?
The validation process starts from 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()
do, you will probably notice that it only calls field.clean(value, initial)
and collects the results into a cleaned_data
dict. So the interesting part is at field.clean, lets 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 to_python
call, followed by validate
and finishing with run_validators
.
So in terms of ModelChoiceField
when you reach the .validate
method, your choice is already a Model instance, thats why, this kind of validation (from Q2) is happening inside the to_python method.
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
one thing i can say is for forms.ChoiceField the input are coming from the user perspective means a user can use inspect element and enter a choice which doesnt appear from the backend .
but for models one the choices are directly coming from the backend or the database
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With