7

Sometimes it is not possible to know beforehand and graciously reject model save inside a validator which shows nice error message in Django Admin.

If a specific crash happens during the save operation (e.g. data integrity error) and we still want to catch it and show a nice error (similar to validation errors), there's no obvious way to do this that I could find.

I tried overriding the save_model method on Django Admin, but this is a horrible thing to do according to the docs:

When overriding ModelAdmin.save_model() and ModelAdmin.delete_model(), your code must save/delete the object. They aren’t meant for veto purposes, rather they allow you to perform extra operations.

What's the correct way to catch specific exceptions and display nice error message then?

UPDATE:

Example: integrity error when using optimistic locking.

More concrete example: ConcurrentTransition error raised by django_fsm when the object's state has been changed in DB since the object was loaded by Admin (this could be considered a light version of optimistic locking).

knaperek
  • 2,113
  • 24
  • 39
  • I may be wrong, but the way you are using django-admin is an extension beyond its original purpose. My reason for stating that is that you need staff status to access. So they aren't meant to support complex workflows and validations. With that being said its a great way to rapidly prototype and extend. So in terms of error catching, IMO, do that with javascript. Its easy to inject custom js in admin - https://stackoverflow.com/questions/19910450/how-to-load-a-custom-js-file-in-django-admin-home. Or you could do a custom save_model. Its not built for that but you could construct it. – sahutchi Oct 02 '17 at 06:13
  • Can you give an example of an issue that couldn't be caught at validation time but which causes integrity errors? – Daniel Roseman Oct 02 '17 at 06:19
  • @sahutchi thanks for your opinion, I know Django Admin isn't the answer for everything, yet I feel this isn't such so dramatic that it couldn't be handled by Admin. – knaperek Oct 02 '17 at 06:50
  • @DanielRoseman I updated the comment to provide a concrete example, but really this doesn't matter that much - I've come across this issue many times in the past, with other runtime exceptions. It is not so hard to imagine, this is really a general issue. – knaperek Oct 02 '17 at 06:52

1 Answers1

4

I found an elegant way to solve this without hacking the save_model method which is not intended for veto purposes.

Instead, it's enough to just override change_view and/or add_view and generate an error message if they crash.

Example:

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

def change_view(self, request, object_id, form_url='', extra_context=None):
    try:
        return super(MyAdmin, self).change_view(request, object_id, form_url, extra_context)
    except MyException as err:
        messages.error(request, err)
        return HttpResponseRedirect(request.path)

edit: This more generic handler catches errors from Admin's AddForm as well:

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

def changeform_view(self, request, *args, **kwargs):
    try:
        return super().changeform_view(request, *args, **kwargs)
    except IOError as err:
        self.message_user(request, str(err), level=messages.ERROR)
        return HttpResponseRedirect(request.path)
knaperek
  • 2,113
  • 24
  • 39
  • Can you elaborate a little more? What's `MyAdmin` here and where to define this function? and where to call this function? –  Jun 03 '20 at 11:55
  • 1
    `MyAdmin` is an example of a custom `ModelAdmin`-derived class that is supposed to handle the error in question. You'd define/override this `changeform_view` (or `change_view`) method on this class, which is itself usually defined in your app/`admin.py` module. – knaperek Jun 11 '20 at 07:40