Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Disable link to edit object in django's admin (display list only)?

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?

like image 592
thornomad Avatar asked Oct 24 '09 18:10

thornomad


People also ask

How do I restrict access to admin pages in Django?

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 .

What is admin site urls in Django?

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/ ).

What is admin Modeladmin in Django?

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.


2 Answers

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.

like image 108
Federico Avatar answered Sep 17 '22 08:09

Federico


Doing this properly requires two steps:

  • Hide the edit link, so nobody stumbles on the detail page (change view) by mistake.
  • Modify the change view to redirect back to the list view.

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.

Hiding the Edit Link

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.

Redirecting to the List 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.

Removing the "Add ITEM" Button

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:

  • disable the "Add item" button
  • prevent people directly adding items by appending /add to the URL
  • prevent bulk delete

Finally you can remove the "Delete selected items" action by modifying the actions parameter.

Putting it all together

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 
like image 40
simpleigh Avatar answered Sep 20 '22 08:09

simpleigh