Як обмежити максимальне значення числового поля в моделі Джанго?


169

Django має різні числові поля, доступні для використання в моделях, наприклад, DecimalField та PositiveIntegerField . Хоча перший може бути обмежений кількістю збережених десяткових знаків і загальною кількістю збережених символів, чи є якийсь спосіб обмежити його зберіганням лише чисел у певному діапазоні, наприклад, 0,0-5,0?

Якщо цього немає, чи є спосіб обмежити поле PositiveIntegerField лише для зберігання, наприклад, чисел до 50?

Оновлення: тепер, коли помилка 6845 закрита , це питання StackOverflow може бути суперечливим. - сампаблокупер


Ви можете створити сигнал попереднього збереження: http://docs.djangoproject.com/en/dev/ref/signals/#django.db.models.signals.pre_save
igorgue

Я повинен був зазначити, що я також хочу, щоб обмеження було застосовано в адміністраторі Джанго. Щонайменше, щоб отримати, щонайменше, документація має таке сказати: docs.djangoproject.com/en/dev/ref/contrib/admin/…
sampablokuper

На насправді, до 1,0 Джанго , здається, був дуже елегантне рішення: cotellese.net/2007/12/11 / ... . Цікаво, чи існує не менш елегантний спосіб зробити це у випуску svn Django.
sampablokuper

Я розчарований, дізнавшись, що , мабуть, не існує елегантного способу зробити це за допомогою поточного svn Django. Докладнішу інформацію див. У цій дискусійній темі: groups.google.com/group/django-users/browse_thread/thread/…
sampablokuper

Використовуйте валідатори на моделі, і перевірка буде працювати в адміністраторському інтерфейсі та в ModelForms: docs.djangoproject.com/en/dev/ref/validators/…
guettli

Відповіді:


133

Ви також можете створити тип власного типу моделі - див. 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))

Але для цього знадобиться набагато більше коду, оскільки ви можете вказати параметр "пропустити" - діапазон (1, 50, 2) - Хоча цікава ідея ...


Це працює, але коли в чистому методі моделі значення цілого числа завжди немає None, що робить його таким, що я не можу забезпечити додаткового очищення до нього. Будь-яка ідея, чому це так, і як це виправити?
KrisF

2
Ви можете покращити своє власне поле, додавши MinValueValidator(min_value) and MaxValueValidator(max_value)перед викликом super().__init__ ...(фрагмент: gist.github.com/madneon/147159f46ed478c71d5ee4950a9d697d )
madneon

349

Ви можете використовувати вбудовані валідатори 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)
        ]
     )

Редагувати : працюючи безпосередньо з моделлю, перед тим, як зберегти модель, обов'язково зателефонуйте методу full_clean , щоб запустити валідатори. Це не потрібно при використанні, ModelFormоскільки форми роблять це автоматично.


4
Я думаю, вам доведеться написати власний валідатор, якщо ви також бажаєте, щоб обмежене_інтеграційне поле було також необов'язковим? (Тільки перевірити діапазон, якщо не порожній) null = True, blank = True цього не зробив ..
radtek

2
У Django 1.7 налаштування працює null=Trueі blank=Trueяк очікувалося. Поле необов’язкове, і якщо воно не заповнене, воно зберігається як нулеве.
Тім Тісдалл

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

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

52

У мене була ця сама проблема; ось моє рішення:

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

10
Використання розуміння списку:models.IntegerField(choices=[(i, i) for i in range(1, n)], blank=True)
Razzi Abuissa

10

Є два способи зробити це. Перша полягає у використанні перевірки форми, щоб ніколи не дозволяти користувачеві вводити будь-яке число старше 50. Документи перевірки форм .

Якщо в процесі не задіяний користувач або ви не використовуєте форму для введення даних, вам доведеться перекрити saveметод моделі, щоб викинути виняток або обмежити дані, що надходять у поле.


2
Ви також можете скористатися формою для підтвердження внесених даних, що не стосуються людини. Це чудово підходить для заповнення Форми як всебічної методики перевірки.
S.Lott

1
Після роздумів над цим я цілком впевнений, що не хочу ставити перевірку у формі. Питання про те, який діапазон чисел прийнятний, є настільки ж частиною моделі, як і питання про те, який тип числа є прийнятним. Мені не хочеться розповідати кожну форму, через яку модель можна редагувати, а також, який діапазон номерів прийняти. Це порушило б DRY, і до того ж це просто недоречно. Тож я буду розбиратися в переважаючому способі збереження моделі або, можливо, створити тип власного поля моделі - якщо я не можу знайти ще кращий спосіб :)
sampablokuper

tghw, ви сказали, що я можу "замінити метод збереження моделі, щоб викинути виняток або обмежити дані, що надходять у поле". Як я міг би зробити з методу переопределення збереження збереження () визначення визначення моделі модель так, що якщо введене число знаходиться за межами заданого діапазону, користувач отримує помилку перевірки, як ніби вона вписала символьні дані в числове поле? Тобто чи я можу це зробити, що буде працювати незалежно від того, редагує користувач через адміністратор чи через іншу форму? Я не хочу обмежувати дані, що надходять у поле, не повідомляючи користувачеві, що відбувається :) Дякую!
sampablokuper

Навіть якщо ви використовуєте «зберегти» метод, це не буде працювати при оновленні таблиці через QuerySet як MyModel.object.filter (BlaBla) .update (BlaBla) не викликатиме , крім так перевірка не буде
Olivier Pons

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

Два параметри - макс і хв, і це дозволяє нулю. Ви можете налаштувати валідатор, якщо вам подобається, позбувшись позначеного, якщо оператор або змінити поле на пусте = False, null = False в моделі. Звичайно, це вимагатиме міграції.

Примітка. Мені довелося додати валідатор, оскільки Django не перевіряє діапазон на PositiveSmallIntegerField, натомість він створює мале число (у постгресах) для цього поля, і ви отримуєте помилку БД, якщо вказане число не відповідає діапазону.

Сподіваюся, це допомагає :) Детальніше про валідатори в Джанго .

PS. Я базував свою відповідь на BaseValidator в django.core.validators, але все інакше, окрім коду.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.