web-dev-qa-db-ja.com

Djangoのmodel.save()がfull_clean()を呼び出さないのはなぜですか?

Djangoのormがモデルの一部として保存されていない限り、モデルで 'full_clean'を呼び出さない正当な理由があるかどうかを知っている人がいたら興味があります。

モデルのsave()メソッドを呼び出しても、full_clean()は自動的に呼び出されないことに注意してください。手動で作成した独自のモデルに対して1ステップのモデル検証を実行する場合は、手動で呼び出す必要があります。 Djangoの完全なクリーンドキュメント

(注:Django 1.6 ...以前のDjango docsはModelFormsについても警告がありました。)

人々がこの振る舞いを望まない理由はありますか?モデルに検証を追加するのに時間がかかった場合、モデルが保存されるたびに検証を実行したいと思うと思います。

私はすべてを適切に機能させる方法を知っています。ただ説明を探しています。

138
Aaron

知る限り、これは後方互換性のためです。除外されたフィールドを持つModelForms、デフォルト値を持つモデル、pre_save()信号などにも問題があります。

興味のあるソース:

54
lqc

互換性を考慮しているため、保存時の自動クリーニングはDjango=カーネルでは有効になっていません。

新しいプロジェクトを開始していて、Modelのデフォルトのsaveメソッドを自動的にクリーンアップする場合は、次の信号を使用して、すべてのモデルが保存される前にクリーンアップを実行できます。

from Django.dispatch import receiver
from Django.db.models.signals import pre_save, post_save

@receiver(pre_save)
def pre_save_handler(sender, instance, *args, **kwargs):
    instance.full_clean()
27
Alfred Huang

full_cleanメソッドを呼び出す最も簡単な方法は、savemodelメソッドをオーバーライドすることです。

def save(self, *args, **kwargs):
    self.full_clean()
    return super(YourModel, self).save(*args, **kwargs)
11
M.Void

レシーバーを宣言するコードを挿入する代わりに、アプリをINSTALLED_APPSセクションsettings.py

INSTALLED_APPS = [
    # ...
    'Django_fullclean',
    # your apps here,
]

その前に、 Django-fullclean PyPIを使用:

pip install Django-fullclean
2
Alfred Huang

少なくとも1つのFK関係があることを確認したいモデルがあり、_null=False_を使用したくない場合思いついたのは、カスタム.clean()および.save()メソッドを追加することです。 .clean()は検証エラーを発生させ、.save()はcleanを呼び出します。この方法により、フォームと他の呼び出しコード、コマンドライン、およびテストの両方から整合性が強制されます。これがないと、(AFAICT)モデルが特別に選択された(デフォルトではない)他のモデルとFK関係を持つことを保証するテストを書く方法がありません。

_class Payer(models.Model):

    name = models.CharField(blank=True, max_length=100)
    # Nullable, but will enforce FK in clean/save:
    payer_group = models.ForeignKey(PayerGroup, null=True, blank=True,)

    def clean(self):
        # Ensure every Payer is in a PayerGroup (but only via forms)
        if not self.payer_group:
            raise ValidationError(
                {'payer_group': 'Each Payer must belong to a PayerGroup.'})

    def save(self, *args, **kwargs):
        self.full_clean()
        return super().save(*args, **kwargs)

    def __str__(self):
        return self.name
_
1
shacker