You need to slightly change your logic if possible. What you need is custom AdminModel.form
. All validation should be done there. See the note for save_model()
:
ModelAdmin.save_model() and ModelAdmin.delete_model() must save/delete
the object, they are not for veto purposes, rather they allow you to
perform extra operations.
But if your circumstances are so that you can't do all validation inside the form I'd subclass ModelAdmin
and override def add_view()
, def change_view()
and def changelist_view()
like so:
from django.contrib import admin
from django import forms
from django.contrib.admin import helpers
from django.contrib.admin.options import csrf_protect_m, IS_POPUP_VAR
from django.utils.translation import ugettext as _
from django.utils.encoding import force_text
# for nonfield errors to show correctly
from django.forms.forms import NON_FIELD_ERRORS
from .models import TestModel
class TestModelAdmin(admin.ModelAdmin):
def save_model(self, request, obj, form, change):
raise Exception('test exception')
@csrf_protect_m
def add_view(self, request, form_url='', extra_context=None):
try:
return super(TestModelAdmin, self).add_view(request, form_url, extra_context)
except Exception as e:
pass
# mimic parent class on error
model = self.model
opts = model._meta
ModelForm = self.get_form(request)
formsets = []
inline_instances = self.get_inline_instances(request, None)
form = ModelForm(request.POST, request.FILES)
form.is_valid()
# make faked nonfield error
# see http://stackoverflow.com/questions/8598247/how-to-append-error-message-to-form-non-field-errors-in-django
form._errors[NON_FIELD_ERRORS] = form.error_class([e.message])
# We may handle exception here (just to save indentation)
adminForm = helpers.AdminForm(form, list(self.get_fieldsets(request)),
self.get_prepopulated_fields(request),
self.get_readonly_fields(request),
model_admin=self)
media = self.media + adminForm.media
inline_admin_formsets = []
for inline, formset in zip(inline_instances, formsets):
fieldsets = list(inline.get_fieldsets(request))
readonly = list(inline.get_readonly_fields(request))
prepopulated = dict(inline.get_prepopulated_fields(request))
inline_admin_formset = helpers.InlineAdminFormSet(inline, formset,
fieldsets, prepopulated, readonly, model_admin=self)
inline_admin_formsets.append(inline_admin_formset)
media = media + inline_admin_formset.media
context = {
'title': _('Add %s') % force_text(opts.verbose_name),
'adminform': adminForm,
'is_popup': IS_POPUP_VAR in request.REQUEST,
'media': media,
'inline_admin_formsets': inline_admin_formsets,
'errors': helpers.AdminErrorList(form, formsets),
'app_label': opts.app_label,
'preserved_filters': self.get_preserved_filters(request),
}
context.update(extra_context or {})
return self.render_change_form(request, context, form_url=form_url, add=True)
admin.site.register(TestModel, TestModelAdmin)
My models.py
:
from django.db import models
class TestModel(models.Model):
text = models.TextField()
You see, there's no easy way of hooking inside save_model()
so you'll have to copy-paste part of form preparation code.