Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django PositiveIntegerField accepting Negatives

I have a model with a PositiveIntegerField in my Django application (views):

class Post(models.Model):
    author = models.ForeignKey('UserProfile')
    creation_date = models.DateTimeField(auto_now_add=True)
    views = models.PositiveIntegerField(default=0)
    tags = models.ManyToManyField('Tag', through="PostTagging", null=False, blank=False)
    rating = models.FloatField(default=0)

However, when I test it, it accepts negative values:

Test:

def test_post_with_negative_views(self):
    test_user = User.objects.get(username='test_student')
    test_user_profile = UserProfile.objects.get(user=test_user)

    post = Post.objects.create(author=test_user_profile, title='Django Testing', content='hello world', views=-10)
    self.assertEquals(post.views, 0)

Fail:

Creating test database for alias 'default' ...
......F.......
=====================================================================
FAIL: test_post_with_negative_views (bark.tets.PostTest)
---------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/ewan/Documents/WAD2/studeso/bark/bark/tests.py", line 58, in test_post_with_negative_views
    self.assertEquals(post.views, 0)
AssertionError: -10 != 0

---------------------------------------------------------------------

FAILED (failures=1)

Am I doing something wrong here?

I've tried testing it with int(-10) and int("-10") incase it was a string formatting error I get a lot.

catavaran's answer including:

post.full_clean()

also fails.

like image 666
Ewan Avatar asked Mar 15 '15 22:03

Ewan


1 Answers

Here is the excerpt from the validating objects chapter of the docs:

Note that full_clean() will not be called automatically when you call your model’s save() method. You’ll need to call it manually when you want to run one-step model validation for your own manually created models.

So validating and saving model should look like this:

post = Post(author=test_user_profile, title='Django Testing',
            content='hello world', views=-10)
post.full_clean()
post.save()

UPDATE: Seems like this validation is turned off for SQLite backend. I found this code in the django.db.backends.sqlite3.operations.DatabaseOperations class.

def integer_field_range(self, internal_type):
    # SQLite doesn't enforce any integer constraints
    return (None, None)

Values from this method are used to build validators for PositiveIntegerField.

As far as I understand this is done for compatibility reasons. So if you want to work with SQLite then you have to manually add the validator:

from django.core.validators import MinValueValidator

class Post(models.Model):
    ...
    views = models.PositiveIntegerField(default=0,
                                        validators=[MinValueValidator(0)])

After this modification the full_clean() should work as expected.

like image 142
catavaran Avatar answered Oct 11 '22 12:10

catavaran