Как ограничить максимальное значение числового поля в модели Django?

Django имеет различные числовые поля, доступные для использования в моделях, например. DecimalField и PositiveIntegerField. Хотя первое может быть ограничено количеством сохраненных десятичных знаков и общим количеством сохраненных символов, существует ли способ ограничить его сохранением только чисел в определенном диапазоне, например. 0.0-5.0?

Если это не так, есть ли способ ограничить PositiveIntegerField, чтобы хранить, например, номера до 50?

Обновление: теперь, когда ошибка 6845 была закрыта, этот вопрос StackOverflow может быть спорным. - sampablokuper

Ответ 1

Вы также можете создать настраиваемый тип поля модели - см. 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))

Но для этого потребуется намного больше кода, так как вы можете указать параметр "skip" - диапазон (1, 50, 2). Интересная идея, хотя...

Ответ 2

Вы можете использовать встроенные валидаторы 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)
        ]
     )

Изменить. Хотя эти работают только в том случае, если вы используете модель в ModelForm, используя модель "самостоятельно", вздох.

Ответ 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)

Ответ 5

Есть два способа сделать это. Один из них заключается в использовании проверки формы, чтобы никогда не вводить какой-либо номер более 50 пользователем. Документы проверки подлинности.

Если в процессе нет пользователя, или вы не используете форму для ввода данных, вам придется переопределить метод модели save, чтобы выбросить исключение или ограничить данные, поступающие в поле.

Ответ 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, и он допускает null. Вы можете настроить валидатор, если хотите, избавившись от выраженного оператора if или измените поле на пустое = False, null = False в модели. Разумеется, это потребует миграции.

Примечание. Я должен был добавить валидатор, потому что Django не проверяет диапазон на PositiveSmallIntegerField, вместо этого он создает небольшое значение (в postgres) для этого поля, и вы получаете ошибку DB, если указанный число не соответствует диапазону.

Надеюсь, что это поможет:) Подробнее о Валидаторы в Django.

PS. Я основывал свой ответ на BaseValidator на django.core.validators, но все отличается, за исключением кода.