20đź‘Ť
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',
)
1đź‘Ť
I’d suggest to reconsider using custom views. With the help of generic DetailView
, you’ll need to write literally two lines of code. The template won’t require much work either. You just extend standard change_form.html template, overriding field_sets
block.
I know how to use the readonly attributes to produces a read-only view, but I don’t know how to produce two views, one read-only and one that allows changes.
You actually can register one model in the admin twice[1], using proxy models. (There’re some inconsistencies with permissions for proxy models, but it may not be a problem in your case.)
It seems to be possible to register multiple admin sites[2], too.
I’d like to reuse as much of the admin interface for this as possible, rather than writing a view from scratch.
Interface reuse as such has little to do with views, being mostly template- and style-related thing. View, however, should provide the template context necessary for interface reuse, as you correctly pointed out.
If you decide to go with multiple views per one ModelAdmin
, then it might be useful for you to check how django-reversion
project implements its admin integration: reversion/admin.py.
References
- [Django]-How do I integrate Ajax with Django applications?
- [Django]-A left outer reverse select_related in Django?
- [Django]-Django TemplateSyntaxError – 'staticfiles' is not a registered tag library
0đź‘Ť
You will need to change template django admin uses for model form. Make it readonly and add a button to original template linked to another url.
Note:
I highly discourage this approach, you will definitely not prevent simultaneous changes. This should be solved with locking.
Also, I recommend using django-reversion for keeping history of objects and eliminating “accidental changes” risk.
- [Django]-Django Migrations Add Field with Default as Function of Model
- [Django]-Django Framework – Is there a shutdown event that can be subscribed to?
- [Django]-Override existing Django Template Tags
0đź‘Ť
You could create a custom view and display your object there.
To create a custom view in an admin module, override the get_urls()
method :
class MyAdmin(admin.ModelAdmin):
…
def get_urls(self):
urls = super(MyAdmin, self).get_urls()
my_urls = patterns('',
url(r'^custom_view/(?P<my_id>\d+)/$', self.admin_site.admin_view(self.custom_viem), name='custom_view')
)
return my_urls + urls
def custom_view(self, request, my_id):
"""Define your view function as usual in views.py
Link to this view using reverse('admin:custom_view')
"""
from myapp import views
return views.custom_view(request, my_id, self)
In views.py :
def custom_view(request, object_id, model_admin):
admin_site = model_admin.admin_site
opts = model_admin.model._meta
my_object = get_object_or_404(MyObject, pk=object_id)
# do stuff
context = {
'admin_site': admin_site.name,
'opts': opts,
'title': _('My custom view'),
'root_path': '%s' % admin_site.root_path,
'app_label': opts.app_label,
'my_object': my_object,
}
return render_to_response('my_template.html', context,
context_instance=RequestContext(request))
In your template, use {% extends "admin/base_site.html" %}
to keep the admin look and feel.
- [Django]-Django i18n: Make sure you have GNU gettext tools
- [Django]-Heroku – Handling static files in Django app
- [Django]-Django error – matching query does not exist
0đź‘Ť
The below code is implementation of read-only admin using proxy models.
Models.py
//real model
class CompetitionEntry(models.Model):
pass
//Proxy model
class ReviewEntry(CompetitionEntry):
class Meta:
proxy = True
def save(self, *args, **kwargs):
pass
admin.py
//editable admin
class CompetitionEntryAdmin(admin.ModelAdmin):
pass
admin.site.register(CompetitionEntry, CompetitionEntryAdmin)
// read-only admin (assign only “change” permission for this)
class ReviewEntryAdmin(admin.ModelAdmin):
pass
admin.site.register(ReviewEntry, ReviewEntryAdmin)
- [Django]-How to use Django 1.8.5 ORM without creating a django project?
- [Django]-Django testing model with ImageField
- [Django]-How to return 404 page intentionally in django