[Django]-Django Admin- disable Editing and remove "Save" buttons for a specific model

14👍

Override the templates/admin/submit_line.html template and make the buttons whatever you want. You can do this for only the specific model by putting it in templates/admin/[app_label]/[model]/submit_line.html.

To conditionally show the default submit line or your custom submit line, override ModelAdmin.change_view, and add a boolean to extra_context:

class MyModelAdmin(admin.ModelAdmin):
    ...
    def change_view(self, request, object_id, extra_context=None):
        if not request.user.is_superuser:
            extra_context = extra_context or {}
            extra_context['readonly'] = True

        return super(MyModelAdmin, self).change_view(request, object_id, extra_context=extra_context)

31👍

For Django 1.11:

def has_add_permission(self, request, obj=None):
    return False

def changeform_view(self, request, object_id=None, form_url='', extra_context=None):
    extra_context = extra_context or {}
    extra_context['show_save_and_continue'] = False
    extra_context['show_save'] = False
    return super(YourModelAdmin, self).changeform_view(request, object_id, extra_context=extra_context)
👤xleon

19👍

The easiest method would be disabling respective permissions in ModelAdmin class. For example, I have a Cart model that I want an admin to only view (list and details) and all I did was to add the following functions to CartAdmin class to disable delete, change and add

class CartAdmin(admin.ModelAdmin):
    list_display = ['listing']

    def has_add_permission(self, request, obj=None):
        return False

    def has_change_permission(self, request, obj=None):
        return False

    def has_delete_permission(self, request, obj=None):
        return False

The three methods has_add_permission, has_change_permission and has_delete_permission are the ones that disable add button, add form, edit form and delete buttons in the admin

Here is a sample output when viewing a record details in the admin that has the above permissions disabled

enter image description here

As you can see the diagram above, you only have close button and the details are not displayed in a form

16👍

I had same problem. I fixed it in admin.py

from django.contrib.admin.templatetags.admin_modify import register, submit_row as original_submit_row

@register.inclusion_tag('admin/submit_line.html', takes_context=True)
def submit_row(context):
''' submit buttons context change '''
ctx = original_submit_row(context)
ctx.update({
    'show_save_and_add_another': context.get('show_save_and_add_another',
                                             ctx['show_save_and_add_another']),
    'show_save_and_continue': context.get('show_save_and_continue',
                                          ctx['show_save_and_continue']),
    'show_save': context.get('show_save',
                             ctx['show_save']),
    'show_delete_link': context.get('show_delete_link', ctx['show_delete_link'])
})
return ctx

In MyModelAdmin class, add following function

@classmethod
def has_add_permission(cls, request):
    ''' remove add and save and add another button '''
    return False

def change_view(self, request, object_id, extra_context=None):
    ''' customize add/edit form '''
    extra_context = extra_context or {}
    extra_context['show_save_and_continue'] = False
    extra_context['show_save'] = False
    return super(MyModelAdmin, self).change_view(request, object_id, extra_context=extra_context)

10👍

Updated answer using Django 1.8 (Python 3 syntax).

There are three things to do:
1) extend the admin change form template, adding an if to conditionally suppress the submit buttons
2) override admin.ModelAdmin.change_view() and set a context var for the template if to read
3) prohibit unwanted POST requests (from DOM hacking, curl/Postman)


MyProject/my_app/templates/admin/my_app/change_form.html

{% extends "admin/change_form.html" %}
{% load admin_modify %}
{% block submit_buttons_top %}{% if my_editable %}{% submit_row %}{% endif %}{% endblock %}
{% block submit_buttons_bottom %}{% if my_editable %}{% submit_row %}{% endif %}{% endblock %}

MyProject/my_app/admin.py (MyModelAdmin)

def change_view(self, request, object_id, form_url='', extra_context=None):
  obj = MyModel.objects.get(pk=object_id)
  editable = obj.get_status() == 'Active'

  if not editable and request.method == 'POST':
    return HttpResponseForbidden("Cannot change an inactive MyModel")

  more_context = {
    # set a context var telling our customized template to suppress the Save button group
    'my_editable': editable,
  }
  more_context.update(extra_context or {})
  return super().change_view(request, object_id, form_url, more_context)

5👍

I had the same problem – the easiest way to do this, is to include some custom JS.

In you admin.py file include

class Media:
    js = ('/static/js/admin.js',)

Then in your admin.js file, include the following JS.

(function($) {
    $(document).ready(function($) {
         $(".submit-row").hide()
    });
})(django.jQuery);

The row is gone – it should work in all versions of Django too.

👤Mark

4👍

This has been possible for a while. The names are has_add_permission, has_change_permission and has_delete_permission. See the django admin documentation for reference. Here is also an example:

@admin.register(Object)
class Admin(admin.ModelAdmin):

    def has_add_permission(self, request):
        return False

    def has_change_permission(self, request, obj=None):
        return False

    def has_delete_permission(self, request, obj=None):
        return False
👤shezi

2👍

Aug, 2022 Update:

You can remove "SAVE" button, "Save and continue editing" button, "Save and add another" button and "Delete" button from a specific admin.

For example, this is "Person" model in "store" app below:

# "store/models.py"

from django.db import models

class Person(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)

    def __str__(self):
        return self.first_name + " " + self.last_name

    class Meta:
        verbose_name = "Person"
        verbose_name_plural = "Person"

Then, this is "Person" admin in "store" app below:

# "store/admin.py"

from django.contrib import admin
from .models import Person

@admin.register(Person)
class PersonAdmin(admin.ModelAdmin):
    pass

Then, this is how "Add person" page looks like:

enter image description here

Then, this is how "Change person" page looks like:

enter image description here

Then, this is how "Select person to change" page looks like:

enter image description here

Then, this is how "Person" admin on "Store administration" page looks like:

enter image description here

First, to remove "SAVE" button, set "False" to "extra_context[‘show_save’]" in "changeform_view()" as shown below:

# "store/admin.py"

from django.contrib import admin
from .models import Person

@admin.register(Person)
class PersonAdmin(admin.ModelAdmin):
    def changeform_view(self, request, object_id=None, form_url='', extra_context=None):
        extra_context = extra_context or {}

        extra_context['show_save'] = False # Here

        return super().changeform_view(request, object_id, form_url, extra_context)

Then, "SAVE" button is removed from "Add person" page and "Change person" page. *Actually, "SAVE" button is replaced with "Close" buttom as shown below:

enter image description here

enter image description here

Next, to remove "Save and continue editing" button, set "False" to "extra_context[‘show_save_and_continue’]" in "changeform_view()" as shown below:

# "store/admin.py"

from django.contrib import admin
from .models import Person

@admin.register(Person)
class PersonAdmin(admin.ModelAdmin):
    def changeform_view(self, request, object_id=None, form_url='', extra_context=None):
        extra_context = extra_context or {}

        extra_context['show_save'] = False
        extra_context['show_save_and_continue'] = False # Here

        return super().changeform_view(request, object_id, form_url, extra_context)

Then, "Save and continue editing" button is removed from "Add person" page and "Change person" page as shown below:

enter image description here

enter image description here

Next, to remove "Save and add another" button, return "False" in "has_add_permission()" as shown below. *After this, "Add person" page can no longer be accessed:

# "store/admin.py"

from django.contrib import admin
from .models import Person

@admin.register(Person)
class PersonAdmin(admin.ModelAdmin):
    def changeform_view(self, request, object_id=None, form_url='', extra_context=None):
        extra_context = extra_context or {}

        extra_context['show_save'] = False
        extra_context['show_save_and_continue'] = False

        return super().changeform_view(request, object_id, form_url, extra_context)

    def has_add_permission(self, request, obj=None): # Here
        return False

Then, "Save and add another" button is removed from "Change person" page as shown below:

enter image description here

Then, "ADD PERSON" button is also removed from "Select person to change" page as shown below:

enter image description here

Then, "➕ADD" button is also removed from "Person" admin on "Store administration" page as shown below:

enter image description here

Next, to remove "Delete" button, set "False" to "extra_context[‘show_delete’]" in "changeform_view()" as shown below:

# "store/admin.py"

from django.contrib import admin
from .models import Person

@admin.register(Person)
class PersonAdmin(admin.ModelAdmin):
    def changeform_view(self, request, object_id=None, form_url='', extra_context=None):
        extra_context = extra_context or {}

        extra_context['show_save'] = False
        extra_context['show_save_and_continue'] = False
        extra_context['show_delete'] = False # Here

        return super().changeform_view(request, object_id, form_url, extra_context)

    def has_add_permission(self, request, obj=None):
        return False

Then, "Delete" button is removed from "Change person" page as shown below:

enter image description here

Actually, you can also remove "Delete" button by returning "False" in "has_delete_permission()" as shown below:

# "store/admin.py"

from django.contrib import admin
from .models import Person

@admin.register(Person)
class PersonAdmin(admin.ModelAdmin):
    def changeform_view(self, request, object_id=None, form_url='', extra_context=None):
        extra_context = extra_context or {}

        extra_context['show_save'] = False
        extra_context['show_save_and_continue'] = False
        # extra_context['show_delete'] = False

        return super().changeform_view(request, object_id, form_url, extra_context)

    def has_add_permission(self, request, obj=None):
        return False

    def has_delete_permission(self, request, obj=None): # Here
        return False

Then, "Delete" button is removed from "Change person" page as shown below:

enter image description here

Then, "Action" select dropdown box is also removed from "Select person to change" page as shown below:

enter image description here

In addition, you can make the fields on "Change person" page unchangeable by returning "False" in "has_change_permission()" as shown below:

# "store/admin.py"

from django.contrib import admin
from .models import Person

@admin.register(Person)
class PersonAdmin(admin.ModelAdmin):
    def changeform_view(self, request, object_id=None, form_url='', extra_context=None):
        extra_context = extra_context or {}

        extra_context['show_save'] = False
        extra_context['show_save_and_continue'] = False
        # extra_context['show_delete'] = False

        return super().changeform_view(request, object_id, form_url, extra_context)

    def has_add_permission(self, request, obj=None):
        return False

    def has_delete_permission(self, request, obj=None): 
        return False

    def has_change_permission(self, request, obj=None): # Here
        return False

Then, the fields on "Change person" page are made unchangeable as shown below:

enter image description here

Then, "✏️Change" button is replaced with "👁️View" for "Person" admin on "Store administration" page as shown below:

enter image description here

In addition, you can remove "Person" admin from "Store administration" page by returning "False" in "has_view_permission()" as shown below:

# "store/admin.py"

from django.contrib import admin
from .models import Person

@admin.register(Person)
class PersonAdmin(admin.ModelAdmin):
    def changeform_view(self, request, object_id=None, form_url='', extra_context=None):
        extra_context = extra_context or {}

        extra_context['show_save'] = False
        extra_context['show_save_and_continue'] = False
        # extra_context['show_delete'] = False

        return super().changeform_view(request, object_id, form_url, extra_context)

    def has_add_permission(self, request, obj=None):
        return False

    def has_delete_permission(self, request, obj=None): 
        return False

    def has_change_permission(self, request, obj=None):
        return False

    def has_view_permission(self, request, obj=None): # Here
        return False

Then, "Person" admin is removed from "Store administration" page as shown below:

enter image description here

Finally, you can replace "changeform_view()" with "render_change_form()" which can also remove "SAVE" button, "Save and continue editing" button and "Delete" button with "context.update()" as shown below:

# "store/admin.py"

from django.contrib import admin
from .models import Person

@admin.register(Person)
class PersonAdmin(admin.ModelAdmin):
    
    # Here
    def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None):
        context.update({
            'show_save': False, # Here
            'show_save_and_continue': False, # Here
            # 'show_delete': False, # Here
        })
        return super().render_change_form(request, context, add, change, form_url, obj)

    def has_add_permission(self, request, obj=None):
        return False

    def has_delete_permission(self, request, obj=None): 
        return False

    def has_change_permission(self, request, obj=None):
        return False

    def has_view_permission(self, request, obj=None):
        return False

0👍

You could try this package Django Admin View Permission. This package adds a view permission for the specified models and handles the other stuff automatically.

0👍

Based on the excellent answer from @mat_gessel, here’s my solution:

The main differences are UX’y:

Also:

  • override change_form.html app-wide, because read_only is such a useful, non-invasive enhancement
  • define has_delete_permission (may not be required by the OP)
  • test request.method != 'GET' to prevent PATCH and friends (not altogether sure if this is required, tbh)

my_app/admin.py

from django.core.urlresolvers import reverse
from django.shortcuts import redirect

from django.contrib import admin
from django.contrib import messages


class MyModelAdmin(admin.ModelAdmin):
    # let's assume two fields...
    fields = (field1, field2)

    def changeform_view(self, request, object_id=None, form_url='', extra_context=None):
        if object_id:
            extra_context = extra_context or {}
            extra_context['read_only'] = True

            if request.method != 'GET':
                messages.error(request, "Cannot edit a MyModel object")
                return redirect(
                    reverse('admin:myapp_mymodel_change', args=[object_id])
                )

        return super(MyModelAdmin, self).changeform_view(request, object_id, extra_context=extra_context)

    def has_delete_permission(self, request, obj=None):
        return False

    def get_readonly_fields(self, request, obj=None):
        if obj:
            # display all fields as text, rather than inputs
            return (field1, field2)
        else:
            return []

admin/change_form.html

{% extends "admin/change_form.html" %}
{% load admin_modify %}
{# remove the save buttons if read_only is truthy #}
{% block submit_buttons_top %}{% if not read_only %}{% submit_row %}{% endif %}{% endblock %}
{% block submit_buttons_bottom %}{% if not read_only %}{% submit_row %}{% endif %}{% endblock %}

(Tested on Django 1.9: heads up: some imports have moved since then, eg reverse)

👤ptim

0👍

To show close button

app_name/admin.py

from django.contrib.admin.templatetags.admin_modify import register, submit_row as original_submit_row

...
class YourappAdmin(admin.ModelAdmin):
...
    @register.inclusion_tag('admin/submit_line.html', takes_context=True)
    def submit_row(context):
        ctx = original_submit_row(context)
        ctx.update({
            "show_close": True,
        })
        return ctx
...

Leave a comment