Raise a validation error in a model's save method in Django Raise a validation error in a model's save method in Django django django

Raise a validation error in a model's save method in Django


Most Django views e.g. the Django admin will not be able to handle a validation error in the save method, so your users will get 500 errors.

You should do validation on the model form or on the model, and raise ValidationError there. Then call save() only if the model form data is 'good enough to save'.


Bastian, I explain to you my code templating, I hope that helps to you:

Since django 1.2 it is able to write validation code on model. When we work with modelforms, instance.full_clean() is called on form validation.

In each model I overwrite clean() method with a custom function (this method is automatically called from full_clean() on modelform validation ):

from django.db import models class Issue(models.Model):    ....    def clean(self):         rules.Issue_clean(self)  #<-- custom function invocationfrom issues import rulesrules.connect()

Then in rules.py file I write bussiness rules. Also I connect pre_save() to my custom function to prevent save a model with wrong state:

from issues.models import Issue

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 errorsdef Issue_pre_save(sender, instance, **kwargs):     instance.clean()     #<-- custom function invocation

Then, modelform calls model's clean method and my custon function check for a right state or raise a error that is handled by model form.

In order to show errors on form, you should include this on form template:

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

The reason is that model validation erros ara binded to non_field_errors error dictionary entry.

When you save or delete a model out of a form you should remember that a error may be raised:

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

Also, you can add errors to a form dictionary on 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  )

Remember that this code is not execute on save() method: Note that full_clean() will not be called automatically when you call your model’s save() method, nor as a result of ModelForm validation. Then, you can add errors to a form dictionary on 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  )


I think this is more clear way to do that for Django 1.2+

In forms it will be raised as non_field_error, in other cases, like DRF you have to check this case manual, because it will be 500 error.

class BaseModelExt(models.Model):is_cleaned = Falsedef clean(self):    # check validation rules here    self.is_cleaned = Truedef save(self, *args, **kwargs):    if not self.is_cleaned:        self.clean()    super().save(*args, **kwargs)