web-dev-qa-db-ja.com

Djangoモデルフィールドの検証

model fieldsの検証はdjangoのどこに行くべきですか?

少なくとも2つの選択肢を指定できます。モデルのオーバーロードされた.save()メソッドまたはmodels.Fieldサブクラスの.to_python()メソッド(これが機能するには、カスタムフィールドを記述する必要があることは明らかです)。

可能なユースケース:

  • 確実に空の文字列がデータベースに書き込まれないようにすることが絶対に必要な場合(blank = Falseキーワード引数はここでは機能しません。フォームの検証専用です)
  • 確認する必要がある場合、「選択」キーワード引数は、管理インターフェースだけでなく、dbレベルでも尊重されます(enumデータ型をエミュレートするようなもの)

モデルにはクラスレベルの属性empty_strings_allowedもあります。フィールドの基本クラスの定義と派生クラスはそれを上手くオーバーライドしますが、データベースレベルに影響を与えないようです。つまり、モデルを構築できます空の文字列フィールドを使用して、データベースに保存します。私は避けたい(はい、それは必要です)。

可能な実装は

フィールドレベルで:

class CustomField(models.CharField):
    __metaclass__ = models.SubfieldBase
    def to_python(self, value):
        if not value:
            raise IntegrityError(_('Empty string not allowed'))
        return models.CharField.to_python(self, value)

モデルレベルで:

class MyModel(models.Model)
    FIELD1_CHOICES = ['foo', 'bar', 'baz']
    field1 = models.CharField(max_length=255, 
               choices=[(item,item) for item in FIELD1_CHOICES])

    def save(self, force_insert=False, force_update=False):
        if self.field1 not in MyModel.FIELD1_CHOICES:
            raise IntegrityError(_('Invalid value of field1'))
        # this can, of course, be made more generic
        models.Model.save(self, force_insert, force_update)

おそらく、私は何かを見逃していますが、これはより簡単に(そしてよりきれいに)できますか?

58
shylent

Djangoには、バージョン1.2以降の モデル検証 システムがあります。

コメントでは、sebpiqは「OK、モデル検証を配置する場所があります... ModelFormを使用している場合にのみ実行されることを除いて! 、あなたは何をすべきですか?full_cleanをどこで呼び出しますか?」

Pythonレベルの検証では、検証がdbレベルで尊重されるようにすることはできません。最も近いのは、おそらくオーバーライドされたsaveメソッドで_full_clean_を呼び出すことです。これは、そのsaveメソッドを呼び出すすべての人がValidationErrorをキャッチして処理する準備ができていることを意味するため、デフォルトでは行われません。

ただし、これを行っても、誰かがqueryset.update()を使用してモデルインスタンスを一括で更新できます。これにより、この検証がバイパスされます。 Djangoは、更新されたすべてのオブジェクトでPythonレベルの検証を実行できる、合理的に効率的なqueryset.update()を実装できません。

DBレベルの整合性を実際に保証する唯一の方法は、DBレベルの制約を使用することです。 ORMを使用して検証を行うには、検証の実施時期(および検証の失敗の処理)をアプリコードの作成者が認識する必要があります。

これが、モデル検証がデフォルトでModelFormでのみ強制される理由です-ModelFormではValidationErrorを処理する明白な方法が既にあるためです。

62
Carl Meyer

これが欲しいと思う->

from Django.db.models.signals import pre_save

def validate_model(sender, **kwargs):
    if 'raw' in kwargs and not kwargs['raw']:
        kwargs['instance'].full_clean()

pre_save.connect(validate_model, dispatch_uid='validate_models')

http://djangosnippets.org/snippets/2319/ からコピー)

6
Darius

これの根本的な問題は、検証がモデルで行われるべきであるということです。これは、かなり以前からDjango(devメーリングリストでフォームモデルを認識した検証を検索)で議論されています。

それはトランクには当たりませんが、Malcolmの "貧しい人のモデル検証ソリューション" は、おそらく自分自身の繰り返しを避けるための最もクリーンなソリューションです。

3
Arthur Debert

私があなたを「明確に」理解している場合-to_pythonの代わりにget_db_prep_save関数をオーバーライドする必要があります

1
Oduvan