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


169

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

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

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



我应该提到,我也希望将限制应用到Django的admin中。为了得到,至少,在文档中有这样一段话:docs.djangoproject.com/en/dev/ref/contrib/admin/...
sampablokuper

实际上,1.0版之前的Django似乎有一个非常优雅的解决方案:cotellese.net/2007/12/11/…。我想知道Django的svn版本中是否有同样优雅的方法来做到这一点。
sampablokuper

我很失望地得知,有似乎是一种优雅的方式与当前的Django SVN做到这一点。有关更多详细信息
请参见此

在模型上,并验证使用验证器会在管理界面和ModelForms工作:docs.djangoproject.com/en/dev/ref/validators/...
guettli

Answers:


133

您还可以创建自定义模型字段类型-请参见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)-有趣的主意...


这是可行的,但是在模型的clean方法中,整数的值始终为None,这使得它无法进行任何额外的清理。知道这是为什么以及如何解决?
2013年

2
您可以通过MinValueValidator(min_value) and MaxValueValidator(max_value)在调用前添加super().__init__ ...(snippet:gist.github.com/madneon/147159f46ed478c71d5ee4950a9d697d
madneon,

348

您可以使用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表格时不需要这样做,因为表格会自动执行。


4
我想,如果您想让limited_integer_field也为可选,您必须编写自己的验证器?(如果不为空,则仅验证范围)null = True,blank = True没有执行此操作
。– radtek

2
在Django 1.7中设置null=Trueblank=True可以按预期工作。该字段是可选的,如果保留为空白,则存储为null。
Tim Tisdall

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

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

52

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

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

10
使用列表理解:models.IntegerField(choices=[(i, i) for i in range(1, n)], blank=True)
Razzi Abuissa '16

10

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

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


2
您也可以使用表格来验证非人工输入。将表单作为一种全面的验证技术来填充它非常有用。
S.Lott

1
在考虑了这一点之后,我非常确定我不想将验证形式化。哪种数字范围可接受的问题与哪种数字范围可接受的问题一样,也是模型的一部分。我不想告诉每个模型可通过其进行编辑的形式,而不必告诉接受的数字范围。这将违反DRY,此外,这完全是不合适的。所以我要寻找到覆盖模型的保存方法,或者创建自定义模型字段类型-除非我能找到一个甚至更好的方法:)
sampablokuper

tghw,您说过我可以“重写模型的save方法来引发异常或限制进入该字段的数据。” 我如何从模型定义的覆盖的save()方法中进行修改,以便如果输入的数字超出给定范围,则用户会收到验证错误,就像她已将字符数据输入数字字段一样?即有什么办法可以做到这一点,不管用户是通过管理员还是通过其他形式进行编辑?我不想只限制进入该领域的数据而不告诉用户正在发生的事情:)谢谢!
sampablokuper,2009年

即使您使用“保存”的方法,通过查询集更新表时像MyModel.object.filter这不会工作不会调用save所以没有检查将进行(布拉布拉).update(布拉布拉)
奥利维尔·庞斯

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,但除代码外,其他所有内容都不同。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.