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

Bas*_*ian 56 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)
Run Code Online (Sandbox Code Playgroud)

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

Ala*_*air 52

大多数Django视图,例如Django管理员将无法处理save方法中的验证错误,因此您的用户将获得500个错误.

您应该在模型表单或模型上进行验证,并ValidationError在那里进行验证.然后save()仅在模型表单数据"足够好以保存"时调用.

  • @bastian,我也喜欢在模型中拥有一切.在编写新表单时很容易忘记业务规则,但如果业务规则在模型中则不会.出于这个原因,我已经在表格中将验证从表格移到了模型中.如果它存在的话,我愿意以更优雅的方式学习新方法.在任何情况下,我都避免在表单上编写验证代码. (9认同)
  • 我不明白为什么验证只能在表单端完成,而不是在模型保存端完成。就像没有其他方法可以创建对象一样。如果您想在不使用表单的情况下实例化和创建对象并且仍想保证某种状态怎么办? (7认同)
  • 可以通过使用验证器或编写`clean()`方法将验证放入模型中.我只是说`save()`方法不是正确的地方.查看[验证对象]上的文档(https://docs.djangoproject.com/en/dev/ref/models/instances/#validating-objects). (6认同)
  • @dabadaba 你可以把验证放在模型的 clean 方法中,我只是说不要把它放在模型的 `save()` 方法中。如果你把验证放在 `save()` 方法中,那么你会从大多数视图中得到 500 个错误,因为它们不会处理 `ValidationError`。请注意,将验证放在 `save()` 方法中并不是绝对保证 - 你仍然可以编写 `Model.objects.filter(...).update(...)` 或导致无效数据的手动 SQL被拯救。 (3认同)
  • 你说得对,我会将我的验证移到表单中,这样更容易。我只是喜欢在模型中包含所有内容的想法。 (2认同)

dan*_*era 25

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

django 1.2开始,它能够在模型上编写验证代码.当我们使用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()
Run Code Online (Sandbox Code Playgroud)

然后在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
Run Code Online (Sandbox Code Playgroud)

然后,modelform调用模型的clean方法和我的custon函数检查正确的状态或引发由模型表单处理的错误.

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

{% if form.non_field_errors %}
      {% for error in form.non_field_errors %}
        {{error}}
      {% endfor %}
{% endif %}  
Run Code Online (Sandbox Code Playgroud)

原因是模型验证错误ara绑定到non_field_errors错误字典条目.

当您从表单中保存或删除模型时,您应该记住可能会引发错误:

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

此外,您可以在没有模型的情况下向表单字典添加错误:

    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  )
Run Code Online (Sandbox Code Playgroud)

请记住,此代码不是在save()方法上执行的:请注意,调用模型的save()方法时,不会自动调用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  )
Run Code Online (Sandbox Code Playgroud)

  • Moltes gràcies 为您提供冗长的解释。我正在寻找自动的东西,Djangoish。您的示例可能对其他情况感兴趣,但我现在正在编写的示例只是 1 行验证,因此我不会在这里实现整个过程。 (2认同)

meg*_*joe 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)
Run Code Online (Sandbox Code Playgroud)

  • 这对我来说似乎非常简单和有效,每当您需要验证以编程方式创建的对象时,即:该过程中不涉及表单提交。谢谢 (2认同)