78

モデルのsaveメソッドで検証エラーを適切に発生させ、ユーザーに明確なメッセージを返す方法がわかりません。

基本的に、「if」の各部分がどのように終了するか、エラーを発生させたい部分、および実際に保存する部分を知りたいです。

def save(self, *args, **kwargs):
    if not good_enough_to_be_saved:
        raise ValidationError
    else:
        super(Model, self).save(*args, **kwargs)

次に、たとえば値が一意でない場合にDjangoが自動的に返すのと同じように、何が問題であるかをユーザーに正確に伝える検証エラーを送信するために何をすべきかを知りたいです。(ModelForm)を使用して、モデルからすべてを調整しています。

4

7 に答える 7

75

ほとんどのDjangoビュー(Django管理者など)は、saveメソッドで検証エラーを処理できないため、ユーザーは500エラーを受け取ります。

モデルフォームまたはモデルで検証を行い、そこでレイズする必要ValidationErrorがあります。save()次に、モデルフォームデータが「保存するのに十分」である場合にのみ呼び出します。

于 2012-01-07T16:18:57.333 に答える
30

バスティアン、私はあなたに私のコードテンプレートを説明します、私はそれがあなたに役立つことを願っています:

django 1.2以降、モデルに検証コードを記述できるようになりました。モデルフォームを操作する場合、フォームの検証時にinstance.full_clean()が呼び出されます。

各モデルclean()で、メソッドをカスタム関数で上書きします(このメソッドは、モデルフォームの検証時にfull_clean()から自動的に呼び出されます)。

from django.db import models
 
class Issue(models.Model):
    ....
    def clean(self): 
        rules.Issue_clean(self)  #<-- custom function invocation

from issues import rules
rules.connect()

次に、rules.pyファイルにビジネスルールを記述します。またpre_save()、カスタム関数に接続して、モデルを間違った状態で保存しないようにします。

からissues.modelsインポートIssue

def connect():    
    from django.db.models.signals import post_save, pre_save, pre_delete
    #issues 
    pre_save.connect(Issue_pre_save, sender = Incidencia ) 
    post_save.connect(Issue_post_save, sender = Incidencia )
    pre_delete.connect(Issue_pre_delete, sender= Incidencia) 

def Incidencia_clean( instance ):    #<-- custom function 
    import datetime as dt    
    errors = {}

    #dia i hora sempre informats     
    if not instance.dia_incidencia:   #<-- business rules
        errors.setdefault('dia_incidencia',[]).append(u'Data missing: ...')
        
    #dia i hora sempre informats     
    if not  instance.franja_incidencia: 
        errors.setdefault('franja_incidencia',[]).append(u'Falten Dades: ...')
 
    #Només es poden posar incidències més ennlà de 7 dies 
    if instance.dia_incidencia < ( dt.date.today() + dt.timedelta( days = -7) ): 
        errors.setdefault('dia_incidencia 1',[]).append(u'''blah blah error desc)''')
 
    #No incidències al futur. 
    if instance.getDate() > datetime.now(): 
        errors.setdefault('dia_incidencia 2',[]).append(u'''Encara no pots ....''') 
    ... 

    if len( errors ) > 0: 
        raise ValidationError(errors)  #<-- raising errors

def Issue_pre_save(sender, instance, **kwargs): 
    instance.clean()     #<-- custom function invocation

次に、modelformはmodelのcleanメソッドを呼び出し、custon関数は正しい状態をチェックするか、modelformによって処理されるエラーを発生させます。

フォームにエラーを表示するには、フォームテンプレートにこれを含める必要があります。

{% if form.non_field_errors %}
      {% for error in form.non_field_errors %}
        {{error}}
      {% endfor %}
{% endif %}  

その理由は、モデル検証エラーがnon_field_errorsエラーディクショナリエントリにバインドされているためです。

フォームからモデルを保存または削除するときは、エラーが発生する可能性があることに注意してください。

try:
    issue.delete()
except ValidationError, e:
    import itertools
    errors = list( itertools.chain( *e.message_dict.values() ) )

また、モデルフォームがない場合は、フォームディクショナリにエラーを追加できます。

    try:
        #provoco els errors per mostrar-los igualment al formulari.
        issue.clean()
    except ValidationError, e:
        form._errors = {}
        for _, v in e.message_dict.items():
            form._errors.setdefault(NON_FIELD_ERRORS, []).extend(  v  )

このコードはsave()メソッドでは実行されないことに注意してください。モデルのsave()メソッドを呼び出したとき、またはModelForm検証の結果として、full_clean()が自動的に呼び出されないことに注意してください。次に、モデルフォームがない場合のフォームディクショナリにエラーを追加できます。

    try:
        #provoco els errors per mostrar-los igualment al formulari.
        issue.clean()
    except ValidationError, e:
        form._errors = {}
        for _, v in e.message_dict.items():
            form._errors.setdefault(NON_FIELD_ERRORS, []).extend(  v  )
于 2012-01-07T16:44:01.887 に答える
7

これは、Django1.2以降でそれを行うためのより明確な方法だと思います

フォームではnon_field_errorとして発生しますが、DRFのように、500エラーになるため、このケースのマニュアルを確認する必要があります。

class BaseModelExt(models.Model):
    is_cleaned = False

    def clean(self):
        # check validation rules here

        self.is_cleaned = True

    def save(self, *args, **kwargs):
        if not self.is_cleaned:
            self.clean()

        super().save(*args, **kwargs)
于 2019-08-20T06:58:16.000 に答える
2

ValueErrorDjangoのドキュメントでは、メソッドでを上げていますが.save、これはおそらく役に立ちます。

https://docs.djangoproject.com/en/3.1/ref/models/instances/

于 2021-03-28T22:46:40.467 に答える
0

clean()モデルで検証を行う場合は、モデルでまたはclean_fieldsメソッドを使用できます。

編集: これらは実行前にdjangoによって呼び出されsave()、検証エラーはユーザーフレンドリーな方法で処理されます。@ Bradを取得していただき、ありがとうございます。

これらcleanclean_fieldsメソッドは、モデルを保存する前にDjangoのフォームバリデーターによって呼び出されます(たとえば、django adminで、この場合、検証エラーは適切に処理されます)が、save()DRFシリアライザーによって自動的に呼び出されないか、カスタムビューを使用している場合はその場合、それらが呼び出されていることを確認する必要があります(または、ロジックをシリアライザーの検証に入れるなど、別の方法で検証する必要があります)。

強調する価値がある:カスタム検証ロジックを直接そこに入れてそこからsave()上げるとValidationError、フォームでうまく機能しません(たとえば、500エラーで管理者を壊します)。これは、django-adminとDRFがうまく連携するには、基本的に、シリアライザーとclean*メソッドの両方で検証ロジックを複製するか、両方で共有できる検証を行うための厄介な方法を見つける必要があります。

ValidationErrorsに関するDjangoのドキュメントはこちら。

于 2021-05-03T10:46:16.757 に答える
-1

User編集:この回答は、プロジェクトを最初から開始しておらず、現在の実装ではカスタムUserクラスがまだ使用されておらず、代わりに把握する必要があるため、現在実装されているクラスを編集できないシナリオがあることを前提としています。Djangoの組み込みのユーザーモデルの動作を変更して、このタスクを実行する方法を説明します。

ほとんどの場合、メソッドをモデルに固定することができcleanますが、組み込みモデルでは必ずしもそのオプションはありませんauth.User。このソリューションを使用すると、クリーンメソッドが呼び出されるフォーム(管理フォームを含む)にsが伝播するようcleanに、モデルのメソッドを作成できます。auth.UserValidationError

次の例では、誰かがauth.User既存のインスタンスと同じメールアドレスを持つようにインスタンスを作成または編集しようとすると、エラーが発生しauth.Userます。免責事項、登録フォームを新しいユーザーに公開する場合は、以下のように検証エラーでユーザー名を呼び出さないようにする必要があります。

from django.contrib.auth.models import User
from django.forms import ValidationError as FormValidationError

def clean_user_email(self):
    instance = self
    super(User, self).clean()
    if instance.email:
        if User.objects.filter(id=instance.id, email=instance.email).exists():
            pass  # email was not modified
        elif User.objects.filter(email=instance.email).exists():
            other_users = [*User.objects.filter(email=instance.email).values_list('username', flat=True)]
            raise FormValidationError(f'At least one other user already has this email address: '
                                      f'{", ".join(other_users)}'
                                      , code='invalid')

# assign the above function to the User.clean method
User.add_to_class("clean", clean_user_email)

私はこれを一番下に持っていmy_app.modelsますが、問題のフォームの前にロードされている場所に貼り付ければ、うまくいくと確信しています。

于 2018-11-06T21:05:41.587 に答える
-3
def clean(self):
    raise ValidationError("Validation Error")

def save(self, *args, **kwargs):
    if some condition:
        #do something here
    else:
        self.full_clean()
    super(ClassName, self).save(*args, **kwargs)
于 2017-12-01T11:38:43.870 に答える