如何创建具有最大和最小限制的 Django 浮动字段?

我在 Django 模型中存储了一些浮点数据,只有一定范围的值是有意义的。因此,我希望在模型和 SQL 约束级别强制实施这些限制。

例如,我想做这样的事情:

class Foo(Model):
myfloat = FloatField(min=0.0, max=1.0)

我想在模型级别上做这件事,而不是在表单级别上。实际上,我可能希望表单级别有一个不同的范围; 例如,在表单级别使用百分比[0,100] ,但在模型中转换为[0,1]。

这可能吗? 如果可能,我该怎么做?

56914 次浏览

If you need constraint on form level you can pass min_value and max_value to form field:

myfloat = forms.FloatField(min_value=0.0, max_value=1.0)

But if you need to move it up to model level you have to extend base models.FloatField class

class MinMaxFloat(models.FloatField):
def __init__(self, min_value=None, max_value=None, *args, **kwargs):
self.min_value, self.max_value = min_value, max_value
super(MinMaxFloat, self).__init__(*args, **kwargs)


def formfield(self, **kwargs):
defaults = {'min_value': self.min_value, 'max_value' : self.max_value}
defaults.update(kwargs)
return super(MinMaxFloat, self).formfield(**defaults)

Then you can use it in models

class Foo(models.Model):
myfloat = MinMaxFloat(min_value=0.0, max_value=1.0)

The answers so far describe how to make forms validate. You can also put validators in the model. Use MinValueValidator and MaxValueValidator.

For example:

from django.core.validators import MaxValueValidator, MinValueValidator


...
weight = models.FloatField(
validators=[MinValueValidator(0.0), MaxValueValidator(1.0)],
)

EDIT:

However, that does not add a SQL constraint.

You can add SQL constraints as described here as CheckConstraints in Meta.constraints.

Combined example:

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


class Foo(Model):
myfloat = FloatField(min=0.0, max=1.0,
# for checking in forms
validators=[MinValueValidator(0.0), MaxValueValidator(1.0)],)


class Meta:
constraints = (
# for checking in the DB
CheckConstraint(
check=Q(myfloat__gte=0.0) & Q(myfloat__lte=1.0),
name='foo_myfloat_range'),
)

It's been quite some time and the community voted answer is good enough, based on Django built-ins, but here is another approach, problem specific, following the Django Project's documentation.

Create a custom validator

# app/models.py or create another file for custom validators, i.e. app/custom_validators.py
from django.core.exceptions import ValidationError
from django.utils.translation import gettext_lazy as _  # use if you support internationalization


def validate_interval(value):
if value < 0.0 or value > 1.0:
raise ValidationError(_('%(value)s must be in the range [0.0, 1.0]'), params={'value': value},)

Use your validator in your models

# app/models.py


class Foo(models.Model):
myfloat = models.FloatField(validators=[validate_interval])

You can even make the validator parametric to be more generic.

EDIT - IMPORTANT

Bare in mind that any validator is invoked during the form's clean() method.

I.E. if you try to directly create an object without using a form, your validator will NOT be invoked.

There are those who don't recommend it but if you want to invoke validators while creating an object override the save method.

# app/models.py


from django.core.exceptions import ValidationError
from django.utils.translation import gettext_lazy as _  # use if you support internationalization




class Foo(models.Model):
def save(self, *args, **kwargs):
self.full_clean()  # or clean
super().save(*args, **kwargs)


def validate_interval(value):
if value < 0.0 or value > 1.0:
raise ValidationError(_('%(value)s must be in the range [0.0, 1.0]'), params={'value': value},)


myfloat = models.FloatField(validators=[validate_interval])