Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django model.full_clean() allows invalid value for IntegerField

I have a some models with inheritance that look something like this:

Class A(models.Model):
    some_text = models.CharField(max_length=100)
    #other fields, not important

Class B(A):
    some_int = models.IntegerField(blank=True, null=True, validators=[MinValueValidator(1), MaxValueValidator(1000)])

Then I fire up the Django shell and do the following:

>>> obj = B()
>>> obj.some_text = 'foo'
>>> obj.some_int = 'bar'
>>> obj.full_clean()

And naturally I get:

>>> ValidationError: {'some_int': [u"'bar' value must be an integer."]}

Good. Then:

>>> obj.some_int = '   ' #whitespace
>>> obj.full_clean()

And I get:

>>> ValidationError: {'some_int': [u"'   ' value must be an integer."]}

Perfect. But then I try an empty string:

>>> obj.some_int = '' #empty string
>>> obj.full_clean()

No ValidationError is raised, but if I try to save the object:

>>> obj.save()

I get:

>>> ValueError: invalid literal for int() with base 10: ''

What's going on here? Is this correct behavior? How is Django checking to see if the value is a valid integer?

I really don't want to customize my model's clean() function to check for this, but it's starting to look like I have no choice.

EDIT: Django 1.6.5, Python 2.7.6, Windows 7 64 bit.

like image 309
Armando Alvarado Avatar asked Jun 29 '26 16:06

Armando Alvarado


2 Answers

the reason of this behavior is commented in clean_fields method :

    def clean_fields(self, exclude=None):
    """
    Cleans all fields and raises a ValidationError containing message_dict
    of all validation errors if any occur.
    """
    if exclude is None:
        exclude = []

    errors = {}
    for f in self._meta.fields:
        if f.name in exclude:
            continue
        # Skip validation for empty fields with blank=True. The developer
        # is responsible for making sure they have a valid value.
        raw_value = getattr(self, f.attname)
        if f.blank and raw_value in f.empty_values:
            continue
        try:
            setattr(self, f.attname, f.clean(raw_value, self))
        except ValidationError as e:
            errors[f.name] = e.error_list

    if errors:
        raise ValidationError(errors)
like image 54
Hasan Ramezani Avatar answered Jul 01 '26 11:07

Hasan Ramezani


You're getting this behavior because you have blank=True. I agree with you that the behavior you're seeing is strange - it's a symptom of the fact that in Django model validation is tied up quite tightly with form validation, even though in principle they can be separate.

Assuming that you actually want blank=True and null=True, I wouldn't worry about this. Forms will do the right thing (convert the empty string in a form to a NULL value in the database), and the model and database won't actually let you save the invalid value. The only issue is that if you have data entry that doesn't involve Django forms, the error will get thrown at save() time rather than at full_clean() time.

Of course, if you don't want to allow NULL values for the field, just set blank=False, null=False and full_clean() will complain about the empty string just like you expect.

like image 45
Kevin Christopher Henry Avatar answered Jul 01 '26 12:07

Kevin Christopher Henry



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!