How to limit the maximum value of a numeric field in a Django model?

后端 未结 6 1553
醉话见心
醉话见心 2020-11-27 09:56

Django has various numeric fields available for use in models, e.g. DecimalField and PositiveIntegerField. Although the former can be restricted to the number of decimal pla

相关标签:
6条回答
  • 2020-11-27 10:28

    You can use Django's built-in validators—

    from django.db.models import IntegerField, Model
    from django.core.validators import MaxValueValidator, MinValueValidator
    
    class CoolModelBro(Model):
        limited_integer_field = IntegerField(
            default=1,
            validators=[
                MaxValueValidator(100),
                MinValueValidator(1)
            ]
         )
    

    Edit: When working directly with the model, make sure to call the model full_clean method before saving the model in order to trigger the validators. This is not required when using ModelForm since the forms will do that automatically.

    0 讨论(0)
  • 2020-11-27 10:30

    Here is the best solution if you want some extra flexibility and don't want to change your model field. Just add this custom validator:

    #Imports
    from django.core.exceptions import ValidationError      
    
    class validate_range_or_null(object):
        compare = lambda self, a, b, c: a > c or a < b
        clean = lambda self, x: x
        message = ('Ensure this value is between %(limit_min)s and %(limit_max)s (it is %(show_value)s).')
        code = 'limit_value'
    
        def __init__(self, limit_min, limit_max):
            self.limit_min = limit_min
            self.limit_max = limit_max
    
        def __call__(self, value):
            cleaned = self.clean(value)
            params = {'limit_min': self.limit_min, 'limit_max': self.limit_max, 'show_value': cleaned}
            if value:  # make it optional, remove it to make required, or make required on the model
                if self.compare(cleaned, self.limit_min, self.limit_max):
                    raise ValidationError(self.message, code=self.code, params=params)
    

    And it can be used as such:

    class YourModel(models.Model):
    
        ....
        no_dependents = models.PositiveSmallIntegerField("How many dependants?", blank=True, null=True, default=0, validators=[validate_range_or_null(1,100)])
    

    The two parameters are max and min, and it allows nulls. You can customize the validator if you like by getting rid of the marked if statement or change your field to be blank=False, null=False in the model. That will of course require a migration.

    Note: I had to add the validator because Django does not validate the range on PositiveSmallIntegerField, instead it creates a smallint (in postgres) for this field and you get a DB error if the numeric specified is out of range.

    Hope this helps :) More on Validators in Django.

    PS. I based my answer on BaseValidator in django.core.validators, but everything is different except for the code.

    0 讨论(0)
  • 2020-11-27 10:35

    I had this very same problem; here was my solution:

    SCORE_CHOICES = zip( range(1,n), range(1,n) )
    score = models.IntegerField(choices=SCORE_CHOICES, blank=True)
    
    0 讨论(0)
  • 2020-11-27 10:37
    from django.db import models
    from django.core.validators import MinValueValidator, MaxValueValidator
    
    size = models.IntegerField(validators=[MinValueValidator(0),
                                           MaxValueValidator(5)])
    
    0 讨论(0)
  • 2020-11-27 10:41

    There are two ways to do this. One is to use form validation to never let any number over 50 be entered by a user. Form validation docs.

    If there is no user involved in the process, or you're not using a form to enter data, then you'll have to override the model's save method to throw an exception or limit the data going into the field.

    0 讨论(0)
  • 2020-11-27 10:46

    You could also create a custom model field type - see http://docs.djangoproject.com/en/dev/howto/custom-model-fields/#howto-custom-model-fields

    In this case, you could 'inherit' from the built-in IntegerField and override its validation logic.

    The more I think about this, I realize how useful this would be for many Django apps. Perhaps a IntegerRangeField type could be submitted as a patch for the Django devs to consider adding to trunk.

    This is working for me:

    from django.db import models
    
    class IntegerRangeField(models.IntegerField):
        def __init__(self, verbose_name=None, name=None, min_value=None, max_value=None, **kwargs):
            self.min_value, self.max_value = min_value, max_value
            models.IntegerField.__init__(self, verbose_name, name, **kwargs)
        def formfield(self, **kwargs):
            defaults = {'min_value': self.min_value, 'max_value':self.max_value}
            defaults.update(kwargs)
            return super(IntegerRangeField, self).formfield(**defaults)
    

    Then in your model class, you would use it like this (field being the module where you put the above code):

    size = fields.IntegerRangeField(min_value=1, max_value=50)
    

    OR for a range of negative and positive (like an oscillator range):

    size = fields.IntegerRangeField(min_value=-100, max_value=100)
    

    What would be really cool is if it could be called with the range operator like this:

    size = fields.IntegerRangeField(range(1, 50))
    

    But, that would require a lot more code since since you can specify a 'skip' parameter - range(1, 50, 2) - Interesting idea though...

    0 讨论(0)
提交回复
热议问题