Here's an answer that literally does what I asked with only a few lines of code and just a couple of template changes:
class MyModelAdmin(admin.ModelAdmin):
fieldsets = [...]
def get_readonly_fields(self, request, obj=None):
if 'edit' not in request.GET:
return <list all fields here>
else:
return self.readonly_fields
Now the usual URL for the change_form will produce a read only change_form, but if you append "?edit=1" to the URL, you will be able to edit.
The change_form template can also be customized depending on whether "?edit=1" is in the URL. To do this, put 'django.core.context_processors.request'
in TEMPLATE_CONTEXT_PROCESSORS
in settings.py
, and then use request.GET.edit
in the template.
For example, to add an "Edit" button when not in edit mode, insert
{% if not request.GET.edit %}
<li><a href="?edit=1">Edit</a></li>
{% endif %}
just after <ul class="object-tools">
in change_form.html
.
As another example, changing change_form.html
to contain
{% if save_on_top and request.GET.edit %}{% submit_row %}{% endif %}
will mean that the submit row will only be shown in edit mode. One can also hide the Delete buttons on inlines, etc, using this method.
For reference, here is what I put in settings.py
:
TEMPLATE_CONTEXT_PROCESSORS = (
'django.contrib.auth.context_processors.auth',
'django.core.context_processors.debug',
'django.core.context_processors.i18n',
'django.core.context_processors.media',
'django.contrib.messages.context_processors.messages',
# Above here are the defaults.
'django.core.context_processors.request',
)