簡體   English   中英

在 Django 中模型的保存方法中引發驗證錯誤

[英]Raise a validation error in a model's save method in Django

我不確定如何在模型的保存方法中正確引發驗證錯誤並向用戶發送明確的消息。

基本上我想知道“if”的每一部分應該如何結束,我想引發錯誤的那一部分和它實際保存的那一部分:

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

然后我想知道如何向用戶發送一個驗證錯誤,該錯誤確切地告訴用戶出了什么問題,就像 Django 自動返回的一個值一樣,如果例如一個值不唯一。 我正在使用 (ModelForm) 並調整模型中的所有內容。

大多數 Django 視圖(例如 Django 管理員)將無法處理 save 方法中的驗證錯誤,因此您的用戶將收到 500 個錯誤。

您應該對模型表單或模型進行ValidationError ,並在那里引發ValidationError 然后僅當模型表單數據“足以保存”時才調用save()

Bastian,我向您解釋我的代碼模板,希望對您有所幫助:

django 1.2 開始,它可以在 model 上編寫驗證代碼 當我們使用模型表單時,在表單驗證時調用 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()連接到我的自定義函數以防止保存狀態錯誤的模型:

from issues.models 導入問題

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 調用模型的 clean 方法,我的自定義函數檢查正確的狀態或引發由模型表單處理的錯誤。

為了在表單上顯示錯誤,您應該在表單模板中包含以下內容:

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

原因是模型驗證錯誤 ara 綁定到 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() 方法時,也不會自動調用 full_clean(),也不會作為 ModelForm 驗證的結果。 然后,您可以在沒有 modelforms的表單字典中添加錯誤:

    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  )

我認為這是 Django 1.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)

一定要導入ValidationError

from django.core.exceptions import ValidationError

在 Django 文檔中,他們在 .save 方法中引發了 ValueError ,它可能對您有用。 https://docs.djangoproject.com/en/3.1/ref/models/instances/

如果要對模型進行驗證,可以在模型上使用clean()clean_fields方法。 這些在執行save()之前由 django 調用,驗證錯誤以用戶友好的方式處理 - django docs here

編輯:這個答案假設您有一個場景不允許您編輯當前實現的User類,因為您不是從頭開始一個項目,當前的實現還沒有使用自定義 User 類,而您必須找出如何通過修改 Django 的內置用戶模型行為來完成此任務。

大多數情況下,您可以將clean方法粘貼到您的模型中,但是對於內置的auth.User模型,您不一定有該選項。 此解決方案將允許您為auth.User模型創建一個clean方法,這樣ValidationError s 將傳播到調用干凈方法的表單(包括管理表單)。

如果有人嘗試創建或編輯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的底部有這個,但我相信它會起作用,只要你把它放在有問題的表單之前加載的某個地方。

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)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM