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)
- [Django]-How to make Django's devserver public ? Is it generally possible?
- [Django]-How do I go straight to template, in Django's urls.py?
- [Django]-Equivalent of PHP "echo something; exit();" with Python/Django?
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
As you can see the diagram above, you only have close button and the details are not displayed in a form
- [Django]-How to add an HTTP header to all Django responses
- [Django]-Use Django ORM as standalone
- [Django]-How to use TailwindCSS with Django?
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)
- [Django]-How to customize default auth login form in Django?
- [Django]-Should I avoid multi-table (concrete) inheritance in Django by any means?
- [Django]-Django.core.exceptions.ImproperlyConfigured: Error loading MySQLdb module: No module named MySQLdb
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)
- [Django]-How to allow only one radio button to be checked?
- [Django]-Find Monday's date with Python
- [Django]-Usage of .to_representation() and .to_internal_value in django-rest-framework?
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.
- [Django]-Annotating a Django queryset with a left outer join?
- [Django]-Sending an SMS to a Cellphone using Django
- [Django]-Disable session creation in Django
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
- [Django]-Uwsgi throws IO error caused by uwsgi_response_write_body_do broken pipe
- [Django]-Django python date time set to midnight
- [Django]-How can I embed django csrf token straight into HTML?
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:
Then, this is how "Change person" page looks like:
Then, this is how "Select person to change" page looks like:
Then, this is how "Person" admin on "Store administration" page looks like:
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:
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:
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:
Then, "ADD PERSON" button is also removed from "Select person to change" page as shown below:
Then, "➕ADD" button is also removed from "Person" admin on "Store administration" page as shown below:
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:
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:
Then, "Action" select dropdown box is also removed from "Select person to change" page as shown below:
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:
Then, "✏️Change" button is replaced with "👁️View" for "Person" admin on "Store administration" page as shown below:
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:
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
- [Django]-What's the reason why Django has SmallIntegerField?
- [Django]-Django's SuspiciousOperation Invalid HTTP_HOST header
- [Django]-Django – No module named _sqlite3
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.
- [Django]-Why does Django REST Framework provide different Authentication mechanisms
- [Django]-Django Rest Framework remove csrf
- [Django]-Django authentication without a password
0👍
Based on the excellent answer from @mat_gessel, here’s my solution:
The main differences are UX’y:
- use messages and redirect (with reversing-admin-urls), rather than
HttpResponseForbidden
to prevent a save - define get_readonly_fields conditionally to prevent un-saveable inputs being displayed
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 preventPATCH
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
)
- [Django]-Generics vs viewset in django rest framework, how to prefer which one to use?
- [Django]-NumPy array is not JSON serializable
- [Django]-405 POST method not allowed
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
...
- [Django]-Django templates folders
- [Django]-'module' object has no attribute 'now' will trying to create a CSV
- [Django]-Django Rest Framework: Dynamically return subset of fields