78

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

基本上我想知道“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()使用自定义函数覆盖方法(此方法在 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 调用模型的 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(),也不会作为 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  )
于 2012-01-07T16:44:01.887 回答
7

我认为这对于 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)
于 2019-08-20T06:58:16.000 回答
2

ValueError在他们提出的方法中的 Django 文档中.save,它可能对您有用。

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

于 2021-03-28T22:46:40.467 回答
0

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

编辑: 这些在执行之前由 django 调用,save()并且以用户友好的方式处理验证错误不正确的,感谢您选择@Brad。

这些cleanclean_fields方法在保存模型之前由 Django 的表单验证器调用(例如,在 django admin 中,在这种情况下,您的验证错误会得到很好的处理),但不会save()被 DRF 序列化程序自动调用,或者如果您正在使用自定义视图,在这种情况下,您必须确保它们被调用(或以另一种方式验证,例如通过将逻辑放入序列化程序的验证中)。

值得强调:如果您将自定义验证逻辑直接放入save()并从那里提出 a ValidationError,这与表单效果不佳(例如,以 500 错误破坏管理员),如果您想要 django-admin 和DRF 可以很好地协同工作......您基本上必须在序列化程序和clean*方法中复制验证逻辑,或者找到一些可以与两者共享的尴尬方式进行验证。

关于 ValidationErrors 的 Django 文档在这里。.

于 2021-05-03T10:46:16.757 回答
-1

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

大多数情况下,您可以将一种方法粘贴clean到您的模型中,但对于内置模型,您不一定拥有该选项auth.User。此解决方案将允许您以s 将传播到调用 clean 方法的表单(包括管理表单)的clean方式为auth.User模型创建一个方法。ValidationError

如果有人尝试创建或编辑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 回答