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

前端 未结 6 1352
余生分开走
余生分开走 2021-01-03 05:54

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

相关标签:
6条回答
  • 2021-01-03 06:36

    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.

    0 讨论(0)
  • 2021-01-03 06:43

    EDIT:

    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.

    OLD ANSWER:

    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.

    0 讨论(0)
  • 2021-01-03 06:43

    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

    0 讨论(0)
  • 2021-01-03 06:50

    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
    
    0 讨论(0)
  • 2021-01-03 06:55

    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)
    
    0 讨论(0)
  • 2021-01-03 06:55

    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.

    0 讨论(0)
提交回复
热议问题