如何限制Django模型中数字字段的最大值?

问题:如何限制Django模型中数字字段的最大值?

Django有各种可用于模型的数字字段,例如DecimalFieldPositiveIntegerField。尽管前者可以限制为存储的小数位数和总字符数,但是有没有办法将其限制为存储一定范围内的数字,例如0.0-5.0?

失败了,有什么方法可以限制PositiveIntegerField只存储例如最大为50的数字吗?

更新:现在,错误6845 已关闭,此StackOverflow问题可能没有意义。-sampablokuper

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 places stored and the overall number of characters stored, is there any way to restrict it to storing only numbers within a certain range, e.g. 0.0-5.0 ?

Failing that, is there any way to restrict a PositiveIntegerField to only store, for instance, numbers up to 50?

Update: now that Bug 6845 has been closed, this StackOverflow question may be moot. – sampablokuper


回答 0

您还可以创建自定义模型字段类型-请参见http://docs.djangoproject.com/en/dev/howto/custom-model-fields/#howto-custom-model-fields

在这种情况下,您可以从内置的IntegerField中“继承”并覆盖其验证逻辑。

我考虑得越多,我意识到这对于许多Django应用程序将很有用。也许IntegerRangeField类型可以作为补丁提交,供Django开发人员考虑添加到主干。

这为我工作:

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)

然后,在模型类中,您将像这样使用它(字段是放置上述代码的模块):

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

对于一个负值和正值范围(例如振荡器范围)进行“或”操作:

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

如果可以用范围运算符这样调用它,那将是一件很酷的事情:

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

但是,这将需要更多代码,因为您可以指定“跳过”参数-range(1,50,2)-有趣的主意…

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…


回答 1

您可以使用Django的内置验证器

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)
        ]
     )

编辑:直接使用模型时,请确保在保存模型之前调用模型full_clean方法以触发验证器。使用ModelForm表格时不需要这样做,因为表格会自动执行。

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.


回答 2

from django.db import models
from django.core.validators import MinValueValidator, MaxValueValidator

size = models.IntegerField(validators=[MinValueValidator(0),
                                       MaxValueValidator(5)])
from django.db import models
from django.core.validators import MinValueValidator, MaxValueValidator

size = models.IntegerField(validators=[MinValueValidator(0),
                                       MaxValueValidator(5)])

回答 3

我有同样的问题。这是我的解决方案:

SCORE_CHOICES = zip( range(1,n), range(1,n) )
score = models.IntegerField(choices=SCORE_CHOICES, blank=True)

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)

回答 4

有两种方法可以做到这一点。一种是使用表单验证,永远不要让用户输入超过50个数字。表单验证文档

如果该过程中没有用户参与,或者您没有使用表单输入数据,那么您将不得不重写模型的save方法以引发异常或限制进入该字段的数据。

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.


回答 5

如果您想要一些额外的灵活性并且不想更改模型字段,这是最好的解决方案。只需添加此自定义验证器:

#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)

它可以这样使用:

class YourModel(models.Model):

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

两个参数是max和min,它允许为空。您可以根据需要通过删除标记的if语句来自定义验证器,或者在模型中将字段更改为blank = False,null = False。当然,这将需要迁移。

注意:我必须添加验证器,因为Django不会在PositiveSmallIntegerField上验证范围,而是为该字段创建一个smallint(在postgres中),并且如果指定的数字超出范围,则会出现DB错误。

希望这会有所帮助:)有关Django中验证程序的更多信息

PS。我的答案基于django.core.validators中的BaseValidator,但除代码外,其他所有内容都不同。

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.