Djangoモデルの数値フィールドの最大値を制限する方法は?


169

Djangoには、DecimalFieldPositiveIntegerFieldなど、モデルで使用できるさまざまな数値フィールドがあります。前者は格納される小数点以下の桁数と格納される文字の総数に制限できますが、特定の範囲内の数値のみを格納するように制限する方法はありますか。

それが失敗した場合、PositiveIntegerFieldを制限して、たとえば50までの数値のみを保存する方法はありますか?

更新:バグ6845 がクローズされたため、このStackOverflowの質問は意味がないかもしれません。-サンパブロクパー



Djangoの管理者にも制限を適用する必要があることを述べたはずです。:少なくとも、ドキュメントが、これは言っている、ことを取得するためのdocs.djangoproject.com/en/dev/ref/contrib/admin/...を
sampablokuper

実際、1.0より前のDjangoには非常に洗練されたソリューションがあったようです:cotellese.net/2007/12/11/…。Djangoのsvnリリースでこれを行うエレガントな方法はあるのでしょうか。
sampablokuper、2009年

現在のDjango svnでこれを行うための洗練された方法がないようであることに私は失望しています。詳細については、このディスカッションスレッドを参照してください。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アプリにとってこれがいかに役立つかを実感します。おそらく、Django開発者がトランクへの追加を検討するためのパッチとして、IntegerRangeFieldタイプを送信できます。

これは私のために働いています:

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'パラメーターを指定できるため、これには多くのコードが必要になります-range(1、50、2)-興味深いアイデア...


これは機能しますが、モデルのcleanメソッドでは、整数の値は常にNoneであるため、追加のクリーニングを提供できません。なぜこれがあり、どのように修正するのですか?
KrisF 2013年

2
MinValueValidator(min_value) and MaxValueValidator(max_value)呼び出す前に追加することで、カスタムフィールドを拡張できますsuper().__init__ ...(snippet: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
limited_integer_fieldもオプションにする場合は、独自のバリデーターを作成する必要があると思いますか?(のみ検証の範囲空白でない場合)はnull = Trueの場合、空白=真はそれをしなかった...
radtek

2
Django 1.7の設定null=Trueblank=True期待どおりに動作します。このフィールドはオプションであり、空白のままにすると、nullとして格納されます。
Tim Tisdall、2017年

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

これを行うには2つの方法があります。1つは、フォーム検証を使用して、ユーザーが50を超える数値を入力できないようにすることです。フォーム検証ドキュメント

プロセスに関与するユーザーがいない場合、またはフォームを使用してデータを入力していない場合は、モデルのsaveメソッドをオーバーライドして例外をスローするか、フィールドに入力するデータを制限する必要があります。


2
フォームを使用して、人間以外の入力を検証することもできます。万能の検証手法としてフォームにデータを入力することは非常に効果的です。
S.Lott、2009年

1
これについて考えた後、検証をフォームに入れたくないと確信しています。どの範囲の数値が許容可能であるかという問題は、どの種類の数値が許容可能であるかという問題と同じくらいモデルの一部です。モデルを編集できるすべてのフォームを伝える必要はありません。受け入れる数値の範囲のみを指定します。これはDRYに違反するだけでなく、明らかに不適切です。私は見つけることができない限り-保存方法モデルのを上書きするか、おそらくカスタムモデルフィールドタイプを作成を検討するつもりだ私はそうでもより良い方法を:)
sampablokuper

tghw、あなたは「モデルのsaveメソッドをオーバーライドして例外をスローしたり、フィールドに入るデータを制限したりできる」と言っていました。モデル定義のオーバーライドされたsave()メソッド内から、入力した数値が特定の範囲外である場合、ユーザーが文字データを数値フィールドに入力した場合と同様に検証エラーを受け取るようにするにはどうすればよいですか?つまり、ユーザーが管理者を介して編集しているか、他のフォームを介して編集しているかに関係なく、これを実行できる方法がありますか?ユーザーに何が起こっているのかをユーザーに知らせずに、フィールドに入るデータを制限したくない:)ありがとう!
sampablokuper、2009年

「保存」メソッドを使用する場合でも、MyModel.object.filter(blabla).update(blabla)がsaveを呼び出さないようにQuerySetを介してテーブルを更新する場合、これは機能しないため、チェックは行われません
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)])

2つのパラメーターはmaxとminで、nullを許可します。マークされたifステートメントを削除するか、フィールドをモデルでblank = False、null = Falseに変更して、必要に応じてバリデーターをカスタマイズできます。もちろん、移行が必要です。

注:DjangoはPositiveSmallIntegerFieldの範囲を検証しないため、バリデーターを追加する必要がありました。代わりに、このフィールドにsmallint(postgresで)が作成され、指定された数値が範囲外の場合、DBエラーが発生します。

これが役に立てば幸いです :) Djangoのバリデーターの詳細。

PS。私はdjango.core.validatorsのBaseValidatorに基づいて答えを出しましたが、コード以外はすべて異なります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.