I would like to hide all the \"Save\" buttons in Django\'s Admin\'s Change Form, for a specific model, when certain conditions are met. Therefore, I override the chang
The other keys are checked for in the passed context except show_save_and_continue
. Django always sets this directly.
'show_save_and_add_another': (
context['has_add_permission'] and not is_popup and
(not save_as or context['add'])
),
You can patch the submit_row
template tag function to first check the context
dictionary for show_save_and_add_another
.
@register.inclusion_tag('admin/submit_line.html', takes_context=True)
def submit_row(context):
"""
Display the row of buttons for delete and save.
"""
change = context['change']
is_popup = context['is_popup']
save_as = context['save_as']
show_save = context.get('show_save', True)
show_save_and_continue = context.get('show_save_and_continue', True)
show_save_and_add_another = context.get('show_save_and_add_another', False)
ctx = Context(context)
ctx.update({
'show_delete_link': (
not is_popup and context['has_delete_permission'] and
change and context.get('show_delete', True)
),
'show_save_as_new': not is_popup and change and save_as,
'show_save_and_add_another': (
context.get('show_save_and_add_another', None) or
(context['has_add_permission'] and not is_popup and
(not save_as or context['add']))
),
'show_save_and_continue': not is_popup and context['has_change_permission'] and show_save_and_continue,
'show_save': show_save,
})
return ctx
Edit
Steps to patch the "admin/submit_line.html" inclusion tag
Create a templatetags
folder at the same level of models.py
and views.py
Create __init__.py
in the templatetags
folder
Copy django/contrib/admin/templatetags/admin_modify.py to templatetags/admin_modify.py
.
Overwrite submit_row
function definition with the one above.
Note that this is applicable for Django 2.0 and below.
For recent Django versions, find a context mix that allows this expression to be False
.e.g.
has_add_permission and not is_popup and
(not save_as or add) and can_save
See values for the names used in the above expression.
As of Django 3.1, the approach from the original question should simply work:
...
extra_context['show_save_and_add_another'] = False
...
See this Django commit for details.
Some additional options:
Set save_as=True
on your ModelAdmin
. As described in the docs, this will replace the "Save and add another" button with a "Save as new" button. This may not be ideal for all cases, but it is the simplest solution as far as I know.
Apply a monkey patch for the has_add_permission
method on your ModelAdmin
, so it returns False
during the call to super().change_view
(or changeform_view
).
Override the TemplateResponse.content
(docs) to simply hide (or remove) the submit-row
element, which contains the buttons, from the HTML.
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. As a result, assuming the other save buttons have been disabled, any changes made to the current object can only be saved as a new object. The current object will remain unchanged.
Basic implementation:
class MyModelAdmin(ModelAdmin):
save_as = True
# your other code here, such as the extended changeform_view
The submit_line.html template shows which context variables are used to show/hide the save and delete buttons. Most of those context variables can be set via the extra_context
in changeform_view
(or change_view
). However, as the OP showed, we cannot simply override show_save_and_add_another
in that manner.
As pointed out in @Oluwafemi-Sule's answer,
show_save_and_add_another
is set in admin_modify.py, which creates the context for submit_line.html.
Upon closer inspection of the source, it is tempting to override the has_add_permission
context variable, because that determines the value of show_save_and_add_another
. However, simply adding has_add_permission=False
to extra_context
does not work in Django < 2.1
, because the change will be undone by the ModelAdmin.render_change_form method.
Fortunately, rather than overriding the template or patching the template tags, we can simply trick Django into thinking that has_add_permission
is False
, using a monkey patch in change_view
:
def change_view(self, request, object_id=None, form_url='', extra_context=None):
# use extra_context to disable the other save (and/or delete) buttons
extra_context = dict(show_save=False, show_save_and_continue=False, show_delete=False)
# get a reference to the original has_add_permission method
has_add_permission = self.has_add_permission
# monkey patch: temporarily override has_add_permission so it returns False
self.has_add_permission = lambda __: False
# get the TemplateResponse from super (python 3)
template_response = super().change_view(request, object_id, form_url, extra_context)
# restore the original has_add_permission (otherwise we cannot add anymore)
self.has_add_permission = has_add_permission
# return the result
return template_response
This also works if you replace change_view
by changeform_view
, as used by the OP (source).
Note: obviously, this option can only be used if has_add_permission=False
does not interfere with other things in your change view.
Based on this example from the docs, it is also possible simply to hide (or even remove) the submit-row
from the rendered HTML for the change_view
:
def change_view(self, request, object_id=None, form_url='',
extra_context=None):
# get the default template response
template_response = super().change_view(request, object_id, form_url,
extra_context)
# here we simply hide the div that contains the save and delete buttons
template_response.content = template_response.rendered_content.replace(
'<div class="submit-row">',
'<div class="submit-row" style="display: none">')
return template_response
As noted, we can also remove the submit-row
section altogether, but that is a bit more work.
This is simpler than option 2, but more fragile.
I propose (changed option 3 of djvg's answer) removing this html input element with regex as with this example:
class MyModelAdmin(admin.ModelAdmin):
def add_view(self, request, form_url='', extra_context=None):
template_response = super(MyModelAdmin, self).add_view(
request, form_url=form_url, extra_context=extra_context)
# POST request won't have html response
if request.method == 'GET':
# removing Save and add another button: with regex
template_response.content = re.sub("<input.*?_addanother.*?(/>|>)", "", template_response.rendered_content)
return template_response
_addanother
is this html element's id
If no one is against, then I publish my little hack.
This can be inserted anywhere and called up from the settings
file (preferably at the very end).
# Hide "save_and_add_another" button
from django.contrib.admin.templatetags import admin_modify
submit_row = admin_modify.submit_row
def submit_row_custom(context):
ctx = submit_row(context)
ctx['show_save_and_add_another'] = False
return ctx
admin_modify.submit_row = submit_row_custom
You may override render_change_form method in ModelAdmin subclass. In this method obj is available as argument and you can check certain conditions.
class OrderAdmin(admin.ModelAdmin):
def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None):
context.update({
'show_save': False,
'show_save_and_continue': False,
'show_delete': False
})
return super().render_change_form(request, context, add, change, form_url, obj)
I have an alternative way to hide the "Save and add another" button.
In form.py
class TestForm(forms.ModelForm):
class Media:
js = ('admin/yourjsfile.js',)
In yourjsfile.js
(function($) {
'use strict';
$(document).ready(function() {
// hide the "Save and add another" button
$("input[name='_addanother']").attr('type','hidden');
});
})(django.jQuery);
I had same issue for hiding that button. I've tested it in Django 1.11 and it did work, but I think there are better ways to achieve it.