Cannot hide “Save and add another” button in Django Admin

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

  1. Create a templatetags folder at the same level of models.py and views.py

  2. Create __init__.py in the templatetags folder

  3. Copy django/contrib/admin/templatetags/admin_modify.py to templatetags/admin_modify.py.

  4. 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.

summary

Some additional options:

  1. 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.

  2. 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).

  3. Override the TemplateResponse.content (docs) to simply hide (or remove) the submit-row element, which contains the buttons, from the HTML.

details option 1

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 

details option 2

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.

details option 3

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.

FLYtheSKY

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.

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)