How to customize django change form redirect url
You can also be interested in:
Sometimes may happen you need to customize a bit more than usual your django admin interface. Maybe because you need a different dashboard approach, or you just need to aggregate functionalities in a way which differs from a pure CRUD interface. Anyway, if possible, you try to keep the change and delete form code of the django admin, because you really are too lazy to rewrite all the add/change/delete stuff.
In such a situation I found myself searching for a way to customize the redirect url after submitting an add/change/delete form, something like the next param you deal with when implementing authentication logic.
Unfortunately such next parameter is not handled in the core code, so you need a bit of work in order to support it, let's see what you need:
- you'll pass the next parameter as a query string parameter
- the add/change/delete views should read this parameter and set it in the view context
- the change/delete templates should add an input field handling the next value
- the add/change/delete views should redirect to the next url if set
The first step it's up to you, just call the change view adding the next param, i.e.
http://localhost:8000/admin/mymodel/5/change/?next=/my-redirect-url/
Let's see the other steps in detail.
Add the next param in the view context
Who manages the admin stuff is clearly the ModelAdmin
class, that you can find in django here:
django/contrib/admin/options.py
So we need to subclass this class and provide the new created class to our models' admin classes. We need to overwrite 2 methods in particular in order to add the next param to the context: render_change_form
(add and change) and render_delete_form
(delete):
from django.contrib.admin import ModelAdmin class ModelAdminWithNext(ModelAdmin): def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None): context.update({'next': request.GET.get('next', None)}) return super(ModelAdminWithNext, self).render_change_form( request, context, add, change, form_url, obj) def render_delete_form(self, request, context): context.update({'next': request.GET.get('next', None)}) return super(ModelAdminWithNext, self).render_delete_form( request, context)
That's it! Now we can handle the next param in the templates.
Add the next parameter in the change/delete templates
It's quite easy indeed, easier for the change_form template because it contains a block which we can use, while the delete template should be completely overwritten. In order to overwrite django admin templates you just need to create a template with the following name in an application listed before contrib.admin
in the INSTALLED_APPS
setting.
The change form template (templates/admin/change_form.html
):
{% extends "admin/change_form.html" %} {% block form_top %} {% if next %} <input type="hidden" name="next" value="{{ next }}" /> {% endif %} {% endblock form_top %}
The delete form template (templates/admin/delete_confirmation.html
):
{% extends "admin/base_site.html" %} {% load i18n admin_urls static %} {% block extrahead %} {{ block.super }} {{ media }} <script type="text/javascript" src="{% static 'admin/js/cancel.js' %}"></script> {% endblock %} {% block bodyclass %}{{ block.super }} app-{{ opts.app_label }} model-{{ opts.model_name }} delete-confirmation{% endblock %} {% block breadcrumbs %} <div class="breadcrumbs"> <a href="{% url 'admin:index' %}">{% trans 'Home' %}</a> › <a href="{% url 'admin:app_list' app_label=opts.app_label %}">{{ opts.app_config.verbose_name }}</a> › <a href="{% url opts|admin_urlname:'changelist' %}">{{ opts.verbose_name_plural|capfirst|escape }}</a> › <a href="{% url opts|admin_urlname:'change' object.pk|admin_urlquote %}">{{ object|truncatewords:"18" }}</a> › {% trans 'Delete' %} </div> {% endblock %} {% block content %} <div class="delete-confirmation-content"> {% if perms_lacking %} <p>{% blocktrans with escaped_object=object %}Deleting the {{ object_name }} '{{ escaped_object }}' would result in deleting related objects, but your account doesn't have permission to delete the following types of objects:{% endblocktrans %}</p> <ul> {% for obj in perms_lacking %} <li>{{ obj }}</li> {% endfor %} </ul> {% elif protected %} <p>{% blocktrans with escaped_object=object %}Deleting the {{ object_name }} '{{ escaped_object }}' would require deleting the following protected related objects:{% endblocktrans %}</p> <ul> {% for obj in protected %} <li>{{ obj }}</li> {% endfor %} </ul> {% else %} <p>{% blocktrans with escaped_object=object %}Are you sure you want to delete the {{ object_name }} "{{ escaped_object }}"? All of the following related items will be deleted:{% endblocktrans %}</p> {% include "admin/includes/object_delete_summary.html" %} <h2>{% trans "Objects" %}</h2> <ul>{{ deleted_objects|unordered_list }}</ul> <form method="post">{% csrf_token %} {% if next %} <input type="hidden" name="next" value="{{ next }}" /> {% endif %} <div> <input type="hidden" name="post" value="yes" /> {% if is_popup %}<input type="hidden" name="{{ is_popup_var }}" value="1" />{% endif %} {% if to_field %}<input type="hidden" name="{{ to_field_var }}" value="{{ to_field }}" />{% endif %} <input type="submit" value="{% trans "Yes, I'm sure" %}" /> <a href="#" class="button cancel-link">{% trans "No, take me back" %}</a> </div> </form> {% endif %} </div> {% endblock %}
This is the template rendered when you try to delete one item. I'll not cover here the steps you need to do the same thing with the changelist delete action.
Handle the redirect to the next url if present
We need to overwrite other 2 methods of the ModelAdmin class, so the entire ModelAdminWithNext class now looks like
from django.contrib.admin import ModelAdmin from django.http import HttpResponseRedirect from django.contrib.admin.options import IS_POPUP_VAR class ModelAdminWithNext(ModelAdmin): def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None): context.update({'next': request.GET.get('next', None)}) return super(ModelAdminWithNext, self).render_change_form( request, context, add, change, form_url, obj) def render_delete_form(self, request, context): context.update({'next': request.GET.get('next', None)}) return super(ModelAdminWithNext, self).render_delete_form( request, context) def response_post_save_change(self, request, obj): """ Figure out where to redirect after the 'Save' button has been pressed when editing an existing object. """ next = request.POST.get('next', None) if next: return HttpResponseRedirect(next) return super(ModelAdminWithNext, self).response_post_save_change( request, obj) def response_post_save_add(self, request, obj): """ Figure out where to redirect after the 'Save' button has been pressed when adding a new object. """ next = request.POST.get('next', None) if next: return HttpResponseRedirect(next) return super(ModelAdminWithNext, self).response_post_save_add( request, obj) def response_delete(self, request, obj_display, obj_id): next = request.POST.get('next', None) if IS_POPUP_VAR not in request.POST and next: return HttpResponseRedirect(next) return super(ModelAdminWithNext, self).response_delete( request, obj_display, obj_id)
Done!
Now just use this class in your models' admin classes:
from django.contrib import admin from myapp.admin import ModelAdminWithNext from .models import Stuff class StuffAdmin(ModelAdminWithNex): list_display = ('name', ) admin.site.register(Stuff, StuffAdmin)
Hasta la proxima!
Your Smartwatch Loves Tasker!
Your Smartwatch Loves Tasker!
Featured
Archive
- 2021
- 2020
- 2019
- 2018
- 2017
- Nov
- Oct
- Aug
- Jun
- Mar
- Feb
- 2016
- Oct
- Jun
- May
- Apr
- Mar
- Feb
- Jan
- 2015
- Nov
- Oct
- Aug
- Apr
- Mar
- Feb
- Jan
- 2014
- Sep
- Jul
- May
- Apr
- Mar
- Feb
- Jan
- 2013
- Nov
- Oct
- Sep
- Aug
- Jul
- Jun
- May
- Apr
- Mar
- Feb
- Jan
- 2012
- Dec
- Nov
- Oct
- Aug
- Jul
- Jun
- May
- Apr
- Jan
- 2011
- Dec
- Nov
- Oct
- Sep
- Aug
- Jul
- Jun
- May