-
Notifications
You must be signed in to change notification settings - Fork 2
Unexpected ValueError instead of ValidationError #74
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Thanks for the report! I'll look into it. |
Can confirm this is a bug triggered by blank=True, if blank=False a validation error is raised instead |
Whats happening here is that Model.full_clean runs all validation routines on all fields and collects the validation errors and then at the end if there are any it throws a master ValidationError with all the collected validation errors. In this case a ValidationError is being thrown in the EnumField.clean method, but the model full_clean proceeds until it his the check constraint validations that eventually call get_db_prep_value which throws the ValueError during type coercion. This exception escapes full_clean causing the problem. This will be a little tricky to correct without changing all errors to ValidationErrors - which may be ok. Its not clear to me if Model.get_db_prep_value and Model.get_prep_value are expected to raise ValidationErrors or not. |
ok. I finally found some concrete guidance in the django docs:
|
Our to_python() function does honor this though. I don't see the harm in extending the ValidationErrors to the db_prep calls though. I will make the change. There's no official documentation I can find about what get_db_prep_value and get_prep_value are supposed to raise, but most of the Django fields call to_python in those functions which is spec'ed to throw ValidationErrors. I think this change should be minimally invasive - I'll go ahead and make it and issue a 3.0.1 release. |
However, can confirm that some django fields will happily throw a TypeError or ValueError out of get_prep_value. Look at IntegerField for example: def get_prep_value(self, value):
value = super().get_prep_value(value)
if value is None:
return None
try:
return int(value)
except (TypeError, ValueError) as e:
raise e.__class__(
"Field '%s' expected a number but got %r." % (self.name, value),
) from e |
I'm now pretty convinced this is a bug in Django's CheckConstraint.validate. There seems to be no attempt to catch ValueError/TypeError and recast to ValidationError - which could result in 500s instead of 400s when full_clean() is invoked in certain paths when using IntegerField and CharField. Instead of modifying EnumField.get_prep_value I'm going to subclass CheckConstraint and report this issue upstream. |
I'm not quite sure if it is relevant here, but for the original Django CharField I figured out that |
Thanks for the tip! That led me down a better path. I was trying to recreate this issue with non-blank invalid enums and couldnt because I do see now that full_clean doesnt run check constraint validation on those. So yes this is very specific to stric enum character fields when blank=True and strict=True where the empty string is also not a valid enumeration value. I will update the empty_values list for fields in that specific case. |
Given the following model
I would have expected
MyModel(my_field="").full_clean()
to throw a ValidationError, but instead it throws a ValueError in the coercion step.Apart from that, kudos to you and your library, that you throw an error at all. Im quite happy that I have found it.
I was growing quite frustrated with the Django CharField based approach wrt. to nullable enum fields. It is infuriating that one is required to set
blank=true
to allow setting those fields to None/Null, which then automatically implies that an empty string is also allowed and it even sets default="" by default. I mean why?!The text was updated successfully, but these errors were encountered: