Armando Alvarado
Armando Alvarado

Reputation: 128

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.

Upvotes: 3

Views: 2282

Answers (3)

Hasan Ramezani
Hasan Ramezani

Reputation: 5194

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)

Upvotes: 5

Kevin Christopher Henry
Kevin Christopher Henry

Reputation: 48962

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.

Upvotes: 2

Gabriel Amram
Gabriel Amram

Reputation: 2790

Well that is because you allowed your field to be blank (and also null). so it is okay and it is valid.

If you don't want it to be that way, just remove the blank=True option or change it to blank=False

Upvotes: 0

Related Questions