在Django中模型的save方法中引发验证错误

时间:2012-01-07 16:09:34

标签: django validation model validationerror

我不确定如何在模型的保存方法中正确引发验证错误,并向用户发回明确的消息。

基本上我想知道“if”的每个部分应该如何结束,我想要引发错误的部分 以及它实际存储的那个:

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

然后我想知道该怎么做才能发送一个验证错误,该错误向用户说明错误,就像Django自动返回的那样,例如,如果某个值不唯一。 我正在使用(ModelForm)并从模型中调整所有内容。

8 个答案:

答案 0 :(得分:45)

大多数Django观看例如Django管理员将无法处理保存方法中的验证错误,因此您的用户将收到500个错误。

您应该对模型表单或模型进行验证,并在那里引发ValidationError。只有当模型表单数据“足以保存”时才调用save()

答案 1 :(得分:24)

巴斯蒂安,我向你解释我的代码模板,我希望这对你有所帮助:

django 1.2 it is able to write validation code on model以来。当我们使用modelforms时,在表单验证时调用instance.full_clean()。

在每个模型中,我使用自定义函数覆盖clean()方法(此方法在modelform验证时自动从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导入问题

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方法和我的custon函数检查正确的状态或引发由模型表单处理的错误。

为了在表单上显示错误,您应该将其包含在表单模板中:

{% 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(),也不会因模型化验证而自动调用。然后,您可以在 no 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  )

答案 2 :(得分:2)

确保也导入ValidationError

from django.core.exceptions import ValidationError

答案 3 :(得分:1)

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)

答案 4 :(得分:0)

我认为这是针对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)

答案 5 :(得分:0)

在 Django 文档中,他们在 .save 方法中引发了 ValueError,它可能对您有用。 https://docs.djangoproject.com/en/3.1/ref/models/instances/

答案 6 :(得分:0)

如果要对模型进行验证,可以在模型上使用 clean()clean_fields 方法。这些在执行 save() 之前由 django 调用,并以用户友好的方式处理验证错误 - django docs here

答案 7 :(得分:-1)

大多数情况下,您只能在模型中使用clean方法,但是内置auth.User模型不一定具有该选项。通过此解决方案,您可以为clean模型创建auth.User方法,以使ValidationError传播到调用clean方法的表单(包括管理表单)。 / p>

如果有人尝试创建或编辑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的底部,但是只要您将其粘贴在有问题的表单之前已加载的位置,我相信它会起作用。