Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Catch exception on save in Django admin?

I have a pre_save signal handler on a bunch of models, which write to a different database. If something goes wrong, I'd like to abort the whole save, or failing that give a message to the user.

Based on Display custom message from signal in the admin, I wrote a mixin with methods like:

class SafeSaveMixin(object):
    def save_model(self, request, *args, **kwargs):
        try:
            return super(SafeSaveMixin, self).save_model(request, *args, **kwargs)
        except Exception as e:
            self.message_user(request, e, messages.ERROR)

This allows me to throw an Exception from the pre_save handler and show the message to the user. The problem is, even though this winds up skipping the actual Model.save(), the admin console doesn't see anything, so it still reports the object as successfully saved.

If I changed the pre_save handler to a post_save handler, that would allow the base Model.save() to occur and at least Django would report the correct state of things, but the information I need in the other database is based on the previous state of the object, so I need to get to it before the save.

I've also considered stuffing the error message into the object itself in the pre_save and pulling it out in the mixin's save_model() -- but this gets more complicated in the other ModelAdmin save methods like save_formset().

Is there any good way to do this?

like image 522
Adam Avatar asked Oct 24 '14 18:10

Adam


People also ask

What is Save () in Django?

save() , django will save the current object state to record. So if some changes happens between get() and save() by some other process, then those changes will be lost.

How do you remove save and add another Django admin?

The simplest option is to set save_as=True on the ModelAdmin . This will replace the "Save and add another" button with a "Save as new" button.

What is Admin ModelAdmin in Django?

One of the most powerful parts of Django is the automatic admin interface. It reads metadata from your models to provide a quick, model-centric interface where trusted users can manage content on your site. The admin's recommended use is limited to an organization's internal management tool.


1 Answers

I've come up with this, which gets mixed in to the ModelAdmin class:

class InternalModelAdminMixin:
    """Mixin to catch all errors in the Django Admin and map them to user-visible errors."""
    def change_view(self, request, object_id, form_url='', extra_context=None):
        try:
            return super().change_view(request, object_id, form_url, extra_context)
        except Exception as e:
            self.message_user(request, 'Error changing model: %s' % e.msg, level=logging.ERROR)
            # This logic was cribbed from the `change_view()` handling here:
            # django/contrib/admin/options.py:response_post_save_add()
            # There might be a simpler way to do this, but it seems to do the job.
            return HttpResponseRedirect(request.path)

This doesn't interfere with the actual model save process, and simply prevents the 500 error redirect. (Note this will disable the debug stacktrace handling. You could add some conditional handling to add that back in).

like image 127
Symmetric Avatar answered Sep 20 '22 12:09

Symmetric