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

nzrxty8p  于 2023-03-20  发布在  Go
关注(0)|答案(7)|浏览(162)

我不确定如何在模型的保存方法中正确地引发验证错误并向用户发送一条明确的消息。
基本上,我想知道“if”的每一部分应该如何结束,我想引发错误的部分和它实际保存的部分:

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

然后我想知道如何发送一个验证错误,准确地告诉用户哪里出了问题,就像Django在某个值不唯一的情况下自动返回的错误一样。我使用了一个(ModelForm),并调整了模型中的所有内容。

j9per5c4

j9per5c41#

大多数Django视图,例如Django管理员,将无法处理save方法中的验证错误,因此您的用户将得到500个错误。
您应该在模型表单、模型的clean方法上进行验证,或者通过向模型字段添加验证器来进行验证。然后仅当模型表单数据有效时才调用save(),在这种情况下,数据“足以保存”。

1qczuiv0

1qczuiv02#

Bastian,我向你解释我的代码模板,我希望这对你有帮助:
从django1.2开始,它可以在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()连接到自定义函数,以防止保存错误状态的模型:
从问题.模型导入问题

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函数检查状态是否正确,或者引发一个错误,由model form处理。
为了在表单上显示错误,您应该在表单模板上包含以下内容:

{% 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  )

请记住,这段代码不会在保存()方法上执行:注意,当你调用模型的保存()方法时,full_clean()不会自动被调用,也不会作为ModelForm验证的结果被调用,然后,你可以在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  )
ffdz8vbo

ffdz8vbo3#

我认为这是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)
8iwquhpp

8iwquhpp4#

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

6ovsh4lw

6ovsh4lw5#

如果要对模型进行验证,可以对模型使用clean()clean_fields方法。

*EDIT:这些是由django在执行save()之前调用的,验证错误以用户友好的方式处理 * 是不正确的,感谢@布拉德的回复。

这些cleanclean_fields方法在保存模型之前被Django的表单验证器调用(例如在django admin中,在这种情况下,您的验证错误会得到很好的处理),但在save()上不会被调用,DRF序列化器会自动调用,或者如果您使用自定义视图,在这种情况下,您必须确保它们被调用(或者用另一种方法验证,例如,将逻辑放入序列化程序的验证中)。
值得强调:如果您将自定义验证逻辑直接放入save()中,并从那里引发ValidationError,那么表单就不能很好地运行(例如,使用500错误中断管理),如果你想让django-admin和DRF一起工作的话,这会让事情变得真实的痛苦。基本上,您必须要么在序列化程序和clean*方法中复制验证逻辑,要么找到一些笨拙的方法来执行验证,以便两者共享。
Django关于验证错误的文档在这里。

bq9c1y66

bq9c1y666#

编辑:这个答案假设您有一个场景不允许您编辑当前实现的User类,因为您不是从零开始一个项目,当前实现还没有使用自定义User类,您必须找出如何通过修改Django内置的User模型行为来完成这个任务。

大多数时候你可以只在你的模型中添加一个clean方法,但是对于内置的auth.User模型,你不需要这样做,这个解决方案允许你为auth.User模型创建一个clean方法,这样ValidationError就可以传播到调用clean方法的表单中(包括管理表单)。
如果有人尝试创建或编辑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的底部有这个,但是我相信只要你把它放在某个地方,在有问题的表单之前加载,它就可以工作。

frebpwbc

frebpwbc7#

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)

相关问题