I have a Django Model which I wish to be only readonly. No adds and edits allowed.
I have marked all fields readonly and overridden has_add_permission in ModelAdmin
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)
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)
Based on the excellent answer from @mat_gessel, here's my solution:
The main differences are UX'y:
HttpResponseForbidden
to prevent a saveAlso:
read_only
is such a useful, non-invasive enhancementrequest.method != 'GET'
to prevent PATCH
and friends (not altogether sure if this is required, tbh)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 []
{% 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
)
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)
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.
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.