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

发布于 2024-07-18 18:11:26 字数 569 浏览 12 评论 0 原文

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

如果做不到这一点,是否有任何方法可以限制 PositiveIntegerField 仅存储例如最多 50 的数字?

更新:现在 Bug 6845 已被关闭,此StackOverflow 问题可能没有实际意义。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

凉栀 2024-07-25 18:11:26

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

< strong>编辑:直接使用模型时,请确保调用模型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.

╰◇生如夏花灿烂 2024-07-25 18:11:26

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

一抹苦笑 2024-07-25 18:11:26
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)])
一人独醉 2024-07-25 18:11:26

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

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)
靖瑶 2024-07-25 18:11:26

有两种方法可以做到这一点。 一是使用表单验证,绝不让用户输入任何超过 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.

别闹i 2024-07-25 18:11:26

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

#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,并且它允许 null。 如果您愿意,您可以通过删除标记的 if 语句或将模型中的字段更改为空白=False、null=False 来自定义验证器。 这当然需要迁移。

注意:我必须添加验证器,因为 Django 不会验证 PositiveSmallIntegerField 上的范围,而是为此字段创建一个smallint(在 postgres 中),如果指定的数字超出范围,您会收到数据库错误。

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

附言。 我的答案基于 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.

落叶缤纷 2024-07-25 18:11:26

例如,您可以使用 MaxValueValidator()MinValueValidator() 对于 models.DecimalField()models.PositiveIntegerField() 设置值的可用范围如下所示:

# "models.py"

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

class Test(models.Model):
    num1 = models.DecimalField(
        max_digits=3,
        decimal_places=1,
        validators=[
            MaxValueValidator(10.0),
            MinValueValidator(0.0)
        ],
    )
    num2 = models.PositiveIntegerField(
        validators=[
            MaxValueValidator(10),
            MinValueValidator(0)
        ],
    )

最后,最好重写num1num2 字段与 表单。 DecimalField()forms.IntegerField() 设置 Django Admin 中值的可用范围,如下所示:

from django.contrib import admin
from .models import Test
from django import forms

class TestForm(forms.ModelForm):
    num1 = forms.DecimalField(
        max_value=10.0, min_value=0.0, step_size=0.1
    )
    num2 = forms.IntegerField(
        max_value=10, min_value=0
    )

@admin.register(Test)
class TestAdmin(admin.ModelAdmin):
    form = TestForm

另外,代码中带有 forms.NumberInput 与上面的代码相同:

from django.contrib import admin
from .models import Test
from django import forms

class TestForm(forms.ModelForm):
    num1 = forms.DecimalField(
        widget=forms.NumberInput(attrs={'max': 10.0, 'min': 0.0, 'step': 0.1})
    )
    num2 = forms.IntegerField(
        widget=forms.NumberInput(attrs={'max': 10, 'min': 0})
    )

@admin.register(Test)
class TestAdmin(admin.ModelAdmin):
    form = TestForm

For example, you can use MaxValueValidator() and MinValueValidator() for models.DecimalField() and models.PositiveIntegerField() to set the available range of the value as shown below:

# "models.py"

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

class Test(models.Model):
    num1 = models.DecimalField(
        max_digits=3,
        decimal_places=1,
        validators=[
            MaxValueValidator(10.0),
            MinValueValidator(0.0)
        ],
    )
    num2 = models.PositiveIntegerField(
        validators=[
            MaxValueValidator(10),
            MinValueValidator(0)
        ],
    )

Finally, you better override num1 and num2 fields with forms.DecimalField() and forms.IntegerField() to set the available range of the value in Django Admin as shown below:

from django.contrib import admin
from .models import Test
from django import forms

class TestForm(forms.ModelForm):
    num1 = forms.DecimalField(
        max_value=10.0, min_value=0.0, step_size=0.1
    )
    num2 = forms.IntegerField(
        max_value=10, min_value=0
    )

@admin.register(Test)
class TestAdmin(admin.ModelAdmin):
    form = TestForm

In addition, the code with forms.NumberInput below is identical to the code above:

from django.contrib import admin
from .models import Test
from django import forms

class TestForm(forms.ModelForm):
    num1 = forms.DecimalField(
        widget=forms.NumberInput(attrs={'max': 10.0, 'min': 0.0, 'step': 0.1})
    )
    num2 = forms.IntegerField(
        widget=forms.NumberInput(attrs={'max': 10, 'min': 0})
    )

@admin.register(Test)
class TestAdmin(admin.ModelAdmin):
    form = TestForm
野侃 2024-07-25 18:11:26

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

In the 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)
浮萍、无处依 2024-07-25 18:11:26

值得一提的是,有时 Django 验证不起作用,因为 Django 验证主要是应用程序级别的验证,而不是数据库级别的验证。 此外,模型验证不会在模型的保存/创建/更新时自动运行。 如果您想在代码中立即验证您的值,那么您需要手动执行此操作 - 使用覆盖 save() 方法:

class UserRating():
    SCORE_CHOICES = (
        (1, _("Terrible")),
        (2, _("Poor")),
        (3, _("Average")),
        (4, _("Very Good")),
        (5, _("Excellent")),
    )
    score = models.PositiveSmallIntegerField(
        choices=SCORE_CHOICES, default=1, 
            validators=[
                MaxValueValidator(5),
                MinValueValidator(1)
            ]
    )
    
    def save(self, *args, **kwargs):
        if int(self.score) < 1 or int(self.score) > 5:
            raise ValidationError('Score must be located between 0 to 5')
        super(UserRating, self).save(*args, **kwargs)
    
    ...

It is worth mentioning that sometimes Django validation doesn't work as Django validation is mostly an application-level validation, not validation at the database level. Also, Model validation is not run automatically on the save/create/update of the model. If you want to validate your values instantly in your code then you need to do it manually — using the override save() method:

class UserRating():
    SCORE_CHOICES = (
        (1, _("Terrible")),
        (2, _("Poor")),
        (3, _("Average")),
        (4, _("Very Good")),
        (5, _("Excellent")),
    )
    score = models.PositiveSmallIntegerField(
        choices=SCORE_CHOICES, default=1, 
            validators=[
                MaxValueValidator(5),
                MinValueValidator(1)
            ]
    )
    
    def save(self, *args, **kwargs):
        if int(self.score) < 1 or int(self.score) > 5:
            raise ValidationError('Score must be located between 0 to 5')
        super(UserRating, self).save(*args, **kwargs)
    
    ...
人生百味 2024-07-25 18:11:26

在 models.py 中的模型列中添加这样的验证器,

class Planogram(models.Model):
    camera = models.ForeignKey(Camera, on_delete=models.CASCADE)
    xtl = models.DecimalField(decimal_places=10, max_digits=11,validators=[MaxValueValidator(1),MinValueValidator(0)])

如果您使用 create 函数来创建对象,则 将其更改为构造函数,如下所示......
并对该对象调用 fullclean() 然后保存..
一切都会完美地进行。

planogram = Planogram(camera_id = camera,xtl=xtl,ytl=ytl,xbr=xbr,ybr=ybr,product_id=product_id)
planogram.full_clean()
planogram.save()

Add validator like this your model column in models.py

class Planogram(models.Model):
    camera = models.ForeignKey(Camera, on_delete=models.CASCADE)
    xtl = models.DecimalField(decimal_places=10, max_digits=11,validators=[MaxValueValidator(1),MinValueValidator(0)])

if you are using create function to create objects change it to constructor like below....
and call fullclean() on that object and then save..
everything will work perfectly.

planogram = Planogram(camera_id = camera,xtl=xtl,ytl=ytl,xbr=xbr,ybr=ybr,product_id=product_id)
planogram.full_clean()
planogram.save()
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文