[Django]-Showing custom model validation exceptions in the Django admin site

35👍

In django 1.2, model validation has been added.

You can now add a "clean" method to your models which raise ValidationError exceptions, and it will be called automatically when using the django admin.

The clean() method is called when using the django admin, but NOT called on save().

If you need to use the clean() method outside of the admin, you will need to explicitly call clean() yourself.

http://docs.djangoproject.com/en/dev/ref/models/instances/?from=olddocs#validating-objects

So your clean method could be something like this:

from django.core.exceptions import ValidationError

class MyModel(models.Model):

    def is_available(self):
        #do check here
        return result

    def clean(self):
        if not self.is_available():
            raise ValidationError('Item already booked for those dates')

I haven’t made use of it extensively, but seems like much less code than having to create a ModelForm, and then link that form in the admin.py file for use in django admin.

👤monkut

6👍

Pretty old post, but I think “use custom cleaning” is still the accepted answer. But it is not satisfactory. You can do as much pre checking as you want you still may get an exception in Model.save(), and you may want to show a message to the user in a fashion consistent with a form validation error.

The solution I found was to override ModelAdmin.changeform_view(). In this case I’m catching an integrity error generated somewhere down in the SQL driver:

from django.contrib import messages
from django.http import HttpResponseRedirect

def changeform_view(self, request, object_id=None, form_url='', extra_context=None):
    try:
        return super(MyModelAdmin, self).changeform_view(request, object_id, form_url, extra_context)
    except IntegrityError as e:
        self.message_user(request, e, level=messages.ERROR)
        return HttpResponseRedirect(form_url)

5👍

The best way is put the validation one field is use the ModelForm… [ forms.py]

class FormProduct(forms.ModelForm):

class Meta:
    model = Product

def clean_photo(self):
    if self.cleaned_data["photo"] is None:
        raise forms.ValidationError(u"You need set some imagem.")

And set the FORM that you create in respective model admin [ admin.py ]

class ProductAdmin(admin.ModelAdmin):
     form = FormProduct

2👍

I’ve also tried to solve this and there is my solution- in my case i needed to deny any changes in related_objects if the main_object is locked for editing.

1) custom Exception

class Error(Exception):
    """Base class for errors in this module."""
    pass

class EditNotAllowedError(Error):
    def __init__(self, msg):
        Exception.__init__(self, msg)

2) metaclass with custom save method- all my related_data models will be based on this:

class RelatedModel(models.Model):
    main_object = models.ForeignKey("Main")

    class Meta:
        abstract = True

    def save(self, *args, **kwargs):
        if self.main_object.is_editable():
            super(RelatedModel, self).save(*args, **kwargs)
        else:
            raise EditNotAllowedError, "Closed for editing"

3) metaform – all my related_data admin forms will be based on this (it will ensure that admin interface will inform user without admin interface error):

from django.forms import ModelForm, ValidationError
...
class RelatedModelForm(ModelForm):
    def clean(self):
    cleaned_data = self.cleaned_data
    if not cleaned_data.get("main_object")
        raise ValidationError("Closed for editing")
    super(RelatedModelForm, self).clean() # important- let admin do its work on data!        
    return cleaned_data

To my mind it is not so much overhead and still pretty straightforward and maintainable.

👤jki

-1👍

from django.db import models
from django.core.exceptions import ValidationError

class Post(models.Model):
is_cleaned = False

title = models.CharField(max_length=255)

def clean(self):
    self.is_cleaned = True
    if something():
        raise ValidationError("my error message")
    super(Post, self).clean()

def save(self, *args, **kwargs):
    if not self.is_cleaned:
        self.full_clean()
    super(Post, self).save(*args, **kwargs)

Leave a comment