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

Posted

技术标签:

【中文标题】如何限制 Django 模型中数值字段的最大值?【英文标题】:How to limit the maximum value of a numeric field in a Django model? 【发布时间】:2010-10-25 08:23:52 【问题描述】:

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

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

更新:既然 Bug 6845 has been closed,这个 *** 问题可能没有实际意义。 - sampablokuper

【问题讨论】:

您可以创建一个预保存信号:http://docs.djangoproject.com/en/dev/ref/signals/#django.db.models.signals.pre_save 我应该提到我还希望在 Django 的管理员中应用限制。为了做到这一点,至少,文档有这样的说法:docs.djangoproject.com/en/dev/ref/contrib/admin/… 实际上,1.0 之前的 Django 似乎有一个非常优雅的解决方案:cotellese.net/2007/12/11/…。我想知道在 Django 的 svn 版本中是否有同样优雅的方法。 我很失望地得知,没有似乎是使用当前 Django svn 执行此操作的一种优雅方式。有关详细信息,请参阅此讨论主题:groups.google.com/group/django-users/browse_thread/thread/… 在模型上使用验证器,验证将在管理界面和 ModelForms 中工作:docs.djangoproject.com/en/dev/ref/validators/… 【参考方案1】:

你可以使用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)
        ]
     )

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

【讨论】:

我想如果您希望limited_integer_field 也是可选的,您必须编写自己的验证器? (仅验证范围,如果不是空白)null=True,blank=True 没有这样做.. 在 Django 1.7 中设置 null=Trueblank=True 按预期工作。该字段是可选的,如果留空,则存储为 null。【参考方案2】:

您还可以创建自定义模型字段类型 - 请参阅 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) - 不过这个想法很有趣......

【讨论】:

这行得通,但是在模型的 clean 方法中,整数的值始终为 None ,因此我无法对其进行任何额外的清理。知道这是为什么以及如何解决吗? 您可以通过在调用super().__init__ ...之前添加MinValueValidator(min_value) and MaxValueValidator(max_value)来增强您的自定义字段(sn-p:gist.github.com/madneon/147159f46ed478c71d5ee4950a9d697d)【参考方案3】:
from django.db import models
from django.core.validators import MinValueValidator, MaxValueValidator

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

【讨论】:

【参考方案4】:

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

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

【讨论】:

使用列表理解:models.IntegerField(choices=[(i, i) for i in range(1, n)], blank=True)【参考方案5】:

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

如果过程中没有用户参与,或者您没有使用表单来输入数据,那么您必须覆盖模型的 save 方法以引发异常或限制进入字段的数据.

【讨论】:

您也可以使用表单来验证非人工输入。将表单填充为一种全方位的验证技术非常有用。 考虑到这一点,我很确定我不想将验证放在表单中。可接受的数字范围的问题与可接受的数字类型的问题一样是模型的一部分。我不想告诉模型可编辑的每个表格,只需要接受哪些数字范围。这将违反 DRY,此外,这完全不合适。因此,我将研究覆盖模型的保存方法,或者可能创建自定义模型字段类型 - 除非我能找到 甚至 更好的方法:) tghw,您说我可以“覆盖模型的保存方法以引发异常或限制进入该字段的数据。”我将如何 - 从模型定义的覆盖 save() 方法中 - 使如果输入的数字超出给定范围,用户会收到验证错误,就像她在数字字段中输入字符数据一样? IE。无论用户是通过管理员还是通过其他形式进行编辑,有什么方法可以做到这一点?我不想只限制进入该字段的数据而不告诉用户发生了什么:) 谢谢! 即使您使用“保存”方法,当通过像 MyModel.object.filter(blabla).update(blabla) 这样的 QuerySet 更新表时,这也不起作用,因此不会调用保存,因此不会进行检查制作【参考方案6】:

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

#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 错误。

希望这会有所帮助 :) 更多关于 Validators in Django.

附言。我的答案基于 django.core.validators 中的 BaseValidator,但除了代码之外,一切都不同。

【讨论】:

【参考方案7】:

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

【讨论】:

感谢您抽出宝贵时间回答问题。由于问题通常会收到多个答案(此问题目前有七个答案),因此建议您为您的答案提供一些背景信息,以描述为什么它与其他答案不同/更受欢迎。

以上是关于如何限制 Django 模型中数值字段的最大值?的主要内容,如果未能解决你的问题,请参考以下文章

Django数据模型——通用字段选项

请问django如何将字符型字段按数值型排序,如‘12,123,1111,133’,排序后结果为12,123,133,1111,在线等

Django 数据模型的字段列表整理

如何在 Django QuerySet 中注释两个字段的最大值

Django数据模型——数据库字段类型

在 django 中,如何对字段上的模型进行排序,然后获取最后一项?