views:

763

answers:

4

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?

+1  A: 

There isn't a supported way to do this.

Looking at the code, it seems that it automatically sets ModelAdmin.list_display_links to the first element if you don't set it to anything. So the easiest way might be to override the __init__ method in your ModelAdmin subclass to unset that attribute on initialization:

class HitAdmin(admin.ModelAdmin):
    list_display = ('user','ip','user_agent','hitcount')
    search_fields = ('ip','user_agent')
    date_hierarchy = 'created'

    def __init__(self, *args, **kwargs):
        super(HitAdmin, self).__init__(*args, **kwargs)
        self.list_display_links = []

This appears to work, after a very cursory test. I can't guarantee that it won't break anything elsewhere, or that it won't be broken by future changes to Django, though.

Edit after comment:

No need to patch the source, this would work:

    def __init__(self, *args, **kwargs):
        if self.list_display_links:
            unset_list_display = True
        else:
            unset_list_display = False
        super(HitAdmin, self).__init__(*args, **kwargs)
        if unset_list_display:
            self.list_display_links = []

But I highly doubt any patch would be accepted into Django, since this breaks something that the code explicitly does at the moment.

Daniel Roseman
Thanks for that - it works with me, as well. Do you see an obvious/pythonic way to patch the source that says _if the child has it set to empty, don't do anything // but if the child hasn't set it at all, do something_ ? Could submit it as a patch ... but all my ideas to patch, at this point, are rather hackish.
thornomad
See my updated answer.
Daniel Roseman
Thanks - maybe if they added another variable to `ModelAdmin` such as your suggested `unset_list_display_links` [True/False] then they could check that on the same `if` statement they are checking the `list_display_links` ... then again, they may just suggest to override it the way you have done.
thornomad
On closer inspection, I noticed that the link is being added now to the select box - so, when you select an item, it opens the page! Ugh.
thornomad
+2  A: 

You could also be ridiculously hacky about it (if you didn't want to fuss with overriding init) and provide a value for the first element that basically looks like this:

</a>My non-linked value<a>

I know, I know, not very pretty, but perhaps less anxiety about breaking something elsewhere since all we're doing is changing markup.

Here's some sample code about how this works:

class HitAdmin(admin.ModelAdmin):
    list_display = ('user_no_link','ip','user_agent','hitcount')

    def user_no_link(self, obj):
        return u'</a>%s<a>' % obj
    user_no_link.allow_tags = True
    user_no_link.short_description = "user"

Side Note: You could also improve the readability of the output (since you don't want it to be a link) by returning return u'%s' % obj.get_full_name() which might be kinda neat depending on your use case.

T. Stone
That's an interesting approach too - while the above `__init__` tactic may break it, it seems a little more intuitive ... but this gives me some ideas, anyway. Thanks.
thornomad
+1  A: 

In your model admin set:

list_display_links = (None,)

That should do it. (Works in 1.1.1 anyway.)

Josh Ourisman
Hmm - I am using the trunk and I get a `TypeError: getattr(): attribute name must be string [02/Nov/2009 12:05:22] "GET /admin/ HTTP/1.1" 500 2524` error when I try that. Interesting.
thornomad
Hmm, interesting. I just checked my project where I'm doing that, and it's actually running 1.1 (r11602). I just tried upping my project to trunk (r11706), and it still seems to work fine.I do however have some other admin stuff going on (which you can see the details of here http://joshourisman.com/2009/10/15/django-admin-awesomeness/) such that my ModelAdmin that has list_display_links set to (None,) is not actually in my admin.py... I don't see why that would make a different to this, however.
Josh Ourisman
It is strange that yours is working; not sure what's different about it ... but, there I am getting pinpoints that line of code every time. Wierd.
thornomad
What happens if you comment out the other settings in your ModelAdmin and just have that?
Josh Ourisman
I have tried it with a completely empty modelAdmin and only: `list_display_links = (None,)` and still get the same TypeError ... if it works for you, though, I feel I must be doing something wrong.
thornomad
Hmm, I just tried adding it to a different project of mine running on Django 1.1.1, and now I'm getting the TypeError too. It's definitely working on the original project however, so I'll take a closer look at it once I get into work.
Josh Ourisman
+3  A: 

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 templatetag code (templatetags/admin_list.py) checks again to see if the list is empty.

Federico
Just found your post and this is working for me too (setting `self.list_diplay_links = (None,)` in the `__init__`. Thanks!
thornomad