In Django's admin, I want disable the links provided on the "select item to change" page so that users cannot go anywhere to edit the item. (I am going to limit what the users can do with this list to a set of drop down actions - no actual editing of fields).
I see that Django has the ability to choose which fields display the link, however, I can't see how I can have none of them.
class HitAdmin(admin.ModelAdmin): list_display = ('user','ip','user_agent','hitcount') search_fields = ('ip','user_agent') date_hierarchy = 'created' list_display_links = [] # doesn't work, goes to default
Any ideas how to get my object list without any links to edit?
Django admin allows access to users marked as is_staff=True . To disable a user from being able to access the admin, you should set is_staff=False . This holds true even if the user is a superuser. is_superuser=True .
If you open a Django project's urls.py file, in the urlpatterns variable you'll see the line path('admin/', admin. site. urls) . This last path definition tells Django to enable the admin site app on the /admin/ url directory (e.g. http://127.0.0.1:8000/admin/ ).
One of the most powerful parts of Django is the automatic admin interface. It reads metadata from your models to provide a quick, model-centric interface where trusted users can manage content on your site. The admin's recommended use is limited to an organization's internal management tool.
I wanted a Log viewer as a list only.
I got it working like this:
class LogEntryAdmin(ModelAdmin): actions = None list_display = ( 'action_time', 'user', 'content_type', 'object_repr', 'change_message') search_fields = ['=user__username', ] fieldsets = [ (None, {'fields':()}), ] def __init__(self, *args, **kwargs): super(LogEntryAdmin, self).__init__(*args, **kwargs) self.list_display_links = (None, )
It is kind of a mix between both answers.
If you just do self.list_display_links = ()
it will show the link, Anyway because the template-tag
code (templatetags/admin_list.py) checks again to see if the list is empty.
Doing this properly requires two steps:
The second part is important: if you don't do this then people will still be able to access the change view by entering a URL directly (which presumably you don't want). This is closely related to what OWASP term an "Insecure Direct Object Reference".
As part of this answer I'll build a ReadOnlyMixin
class that can be used to provide all the functionality shown.
Django 1.7 makes this really easy: you just set list_display_links
to None
.
class ReadOnlyMixin(): # Add inheritance from "object" if using Python 2 list_display_links = None
Django 1.6 (and presumably earlier) don't make this so simple. Quite a lot of answers to this question have suggested overriding __init__
in order to set list_display_links
after the object has been constructed, but this makes it harder to reuse (we can only override the constructor once).
I think a better option is to override Django's get_list_display_links
method as follows:
def get_list_display_links(self, request, list_display): """ Return a sequence containing the fields to be displayed as links on the changelist. The list_display parameter is the list of fields returned by get_list_display(). We override Django's default implementation to specify no links unless these are explicitly set. """ if self.list_display_links or not list_display: return self.list_display_links else: return (None,)
This makes our mixin easy to use: it hides the edit link by default but allows us to add it back in if required for a particular admin view.
We can change the behaviour of the detail page (change view) by overriding the change_view
method. Here's an extension to the technique suggested by Chris Pratt which automatically finds the right page:
enable_change_view = False def change_view(self, request, object_id, form_url='', extra_context=None): """ The 'change' admin view for this model. We override this to redirect back to the changelist unless the view is specifically enabled by the "enable_change_view" property. """ if self.enable_change_view: return super(ReportMixin, self).change_view( request, object_id, form_url, extra_context ) else: from django.core.urlresolvers import reverse from django.http import HttpResponseRedirect opts = self.model._meta url = reverse('admin:{app}_{model}_changelist'.format( app=opts.app_label, model=opts.model_name, )) return HttpResponseRedirect(url)
Again this is customisable - by toggling enable_change_view
to True
you can switch the details page back on.
Finally, you might want to override the following methods in order to prevent people adding or deleting new items.
def has_add_permission(self, request): return False def has_delete_permission(self, request, obj=None): return False
These changes will:
/add
to the URLFinally you can remove the "Delete selected items" action by modifying the actions
parameter.
Here's the completed mixin:
from django.core.urlresolvers import reverse from django.http import HttpResponseRedirect class ReadOnlyMixin(): # Add inheritance from "object" if using Python 2 actions = None enable_change_view = False def get_list_display_links(self, request, list_display): """ Return a sequence containing the fields to be displayed as links on the changelist. The list_display parameter is the list of fields returned by get_list_display(). We override Django's default implementation to specify no links unless these are explicitly set. """ if self.list_display_links or not list_display: return self.list_display_links else: return (None,) def change_view(self, request, object_id, form_url='', extra_context=None): """ The 'change' admin view for this model. We override this to redirect back to the changelist unless the view is specifically enabled by the "enable_change_view" property. """ if self.enable_change_view: return super(ReportMixin, self).change_view( request, object_id, form_url, extra_context ) else: opts = self.model._meta url = reverse('admin:{app}_{model}_changelist'.format( app=opts.app_label, model=opts.model_name, )) return HttpResponseRedirect(url) def has_add_permission(self, request): return False def has_delete_permission(self, request, obj=None): return False
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With