Modifying the Django Admin: redirects after adding, changing, and deleting
Oct 27, 2008

One of the Django projects that I've been working on for about a year and and will (fingers crossed) be going live in the very very very near future has involved a lot of modification to Django's admin interface. I plan on writing more about the many, many specific changes that I've made to the interface (without modifying the actual Django codebase, so that the changes can be easily applied by anyone without breaking updates), but to talk about them all at once would make far too long of a post. So I'll be taking them on one at a time. The change I want to talk about right now involves redirecting the user to the page I want them to be at after they've finished adding or editing an object rather than to that object's model's change_list. Normally, if you're editing an Entry object in the Blog app, when you hit save it will take you to /admin/blog/entry/, which is a list of all the Entry objects in the database. However there are some instances in which this isn't the behavior I want. Once such instance involves model inheritance. Say, for example, you have multiple types of Entries which you've accomodated through multi-table-inheritance. Because the different sub-classes are different models, they all have their own change_lists in the Django admin. But I want to be able to view, edit, and create Entries of all types from one page. Fortunately, Django makes this fairly easy to accomplish. All that is necessary is to override the appropriate methods in the Entry ModelAdmin. That will end up looking something like this: class EntryAdmin(admin.ModelAdmin): def change_view(self, request, object_id, extra_context=None): result = super(EntryAdminAdmin, self).change_view(request, object_id, extra_context) if not request.POST.has_key('_addanother') and not request.POST.has_key('_continue'): result['Location'] = iri_to_uri("/admin/blog/entry/") return result The exact same modification should be made to add_view as well, and a nearly identical modification to delete_view though it doesn't need to deal with the _addanother and _continue cases. You can then use the EntryAdmin class for all of your varioud Entry sub-classes, or, if you need some other changes to the admin for different Entries sub-classes you can sub-class EntryAdmin for them. Now, whenever you hit the save button after editing any sort of Entry, it will always take you back to /admin/blog/entry/ rather than /admin/blog/linkentry/ or whatever your other subclasses are. If you want it to only take you back to /admin/blog/entry/ if you're coming from a particular page and otherwise take you to /admin/blog/linkentry/ all you need is to add a GET variable to your url (something like '/admin/blog/linkentry/add/?return_to_main=True') and then check for it in your modified change_view, add_view, and delete_view methods with a request.GET.get('return_to_main', False). I've even used this between objects of different model types to create a 'dashboard' page that allows you to view, and alter the relationships between an object of one type with objects of another type. All that's necessary in a case like that is to pass the id of the object in your GET variable and take that into account when creating your uri. An added benefit of that it makes it easy to auto-fill the ForeignKey field when creating a related object. In such a case you'll also need to keep that GET variable in the URLs down the line in order to maintain compatilibility with the 'Save and add another' and 'Save and continue editing' features. But that's still a simple modification: class EntryAdmin(admin.ModelAdmin): def change_view(self, request, object_id, extra_context=None): result = super(EntryAdminAdmin, self).change_view(request, object_id, extra_context) other_id = request.GET.get("other_id", None) if not request.POST.has_key('_addanother') and not request.POST.has_key('_continue'): if other_id: result['Location'] = iri_to_uri("/admin/dashboard/%s/") % other_id return result elif request.POST.has_key('_continue'): if other_id: result['Location'] = iri_to_uri("?other_id=%s" % other_id) return result elif request.POST.has_key('_addanother'): if other_id: result['Location'] = iri_to_uri("%s?other_id=%s" % (result['Location'], other_id)) return result return result But more on that sort of thing in other posts.
blog comments powered by Disqus