202

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

如果做不到这一点,有没有办法限制 PositiveIntegerField 只存储例如最多 50 个数字?

更新:现在 Bug 6845已经关闭,这个 StackOverflow 问题可能没有实际意义。- sampablokuper

4

7 回答 7

445

你可以使用Django 的内置验证器——</p>

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不需要这样做,因为表单会自动执行此操作。

于 2012-08-19T13:32:39.440 回答
145

您还可以创建自定义模型字段类型 - 请参阅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)

OR 对于负数和正数范围(如振荡器范围):

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

如果可以像这样使用范围运算符调用它,那将是非常酷的:

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

但是,这将需要更多代码,因为您可以指定一个“跳过”参数 - range(1, 50, 2) - 不过这个想法很有趣......

于 2009-05-11T18:31:21.033 回答
102
from django.db import models
from django.core.validators import MinValueValidator, MaxValueValidator

size = models.IntegerField(validators=[MinValueValidator(0),
                                       MaxValueValidator(5)])
于 2012-08-19T13:54:25.240 回答
57

我遇到了同样的问题;这是我的解决方案:

SCORE_CHOICES = zip( range(1,n), range(1,n) )
score = models.IntegerField(choices=SCORE_CHOICES, blank=True)
于 2010-10-28T21:38:25.917 回答
10

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

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

于 2009-05-11T17:39:52.023 回答
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,但除了代码之外,一切都不同。

于 2014-09-17T20:45:53.383 回答
0

在forms.py

Class FloatForm(forms.ModelForm):

    class Meta:
        model = Float
        fields = ('name','country', 'city', 'point', 'year')

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields['point'] = forms.FloatField(max_value=100, min_value=1)
于 2021-03-13T10:32:33.840 回答