Show a model on the admin list index page

105 views
Skip to first unread message

Andrea

unread,
Oct 7, 2014, 6:54:42 AM10/7/14
to django...@googlegroups.com

Let's suppose I have a Foo app with a Bar model with a owner field. I want a user to be able to edit all the instances for which obj.owner == request.user.

The model appears correctly in the admin panel for superusers and for users for which I have explicitly assigned the permission change_bar or add_bar, as explained here:

Assuming you have an application with an app_label foo and a model named Bar, to test for basic permissions you should use:

add: user.has_perm('foo.add_bar')
change: user.has_perm('foo.change_bar')
delete: user.has_perm('foo.delete_bar')

How can I show the model Bar in the admin index list without explicitly assigning foo.change_bar or foo.add_bar to the user?

So far I tried the following, expecting the Bar model to appear in the index list page, but it didn't work.

class BarAdmin(admin.ModelAdmin):
    def get_queryset(self, request):
        qs = super(BarAdmin, self).get_queryset(request)
        if request.user.is_superuser:
            return qs        
        return qs.filter(owner=request.user)

    def has_add_permission(self, request):
        return True

    def has_change_permission(self, request, obj=None):
        if obj is None:
            return True
        if obj.owner == request.user:
            return True
        return False

    def has_delete_permission(self, request, obj=None):
        if obj is None:
            return True
        if obj.owner == request.user:
            return True
        return False

    def has_module_permission(self, request):
        return True

Accessing the link admin/foo/bar/ works correctly for every user and returns the list of Bar instances for which obj.owner == request.user. admin/foo/bar/add allows the user to add a new object correctly. These links are although not displayed in the admin index page: which is the function that triggers the appearance of the model in the index page? admin/foo/ returns 403 Forbidden.

I'm using Django 1.7

Thanks,

Andrea

Daniel Rus Morales

unread,
Oct 7, 2014, 9:03:28 AM10/7/14
to django...@googlegroups.com
Hi Andrea,

I answer below in between lines.

On 07 Oct 2014, at 08:53, Andrea <andre...@gmail.com> wrote:

Let's suppose I have a Foo app with a Bar model with a owner field. I want a user to be able to edit all the instances for which obj.owner == request.user.

The model appears correctly in the admin panel for superusers and for users for which I have explicitly assigned the permission change_bar or add_bar, as explained here:

Assuming you have an application with an app_label foo and a model named Bar, to test for basic permissions you should use:

add: user.has_perm('foo.add_bar')
change: user.has_perm('foo.change_bar')
delete: user.has_perm('foo.delete_bar')

How can I show the model Bar in the admin index list without explicitly assigning foo.change_bar or foo.add_bar to the user?



You can’t. The admin interface of Django assumes that when a user has access to the administrative interface of an App-Model it’s not to act as a mere passive consumer with read-only access but rather as an active admin over the content (add, change, delete). An administrator who does only inspect or supervise the content doesn’t fit in the sort of administrator that the Django administration app allows. It’s like allowing an administrator who actually does not administer. 

So far I tried the following, expecting the Bar model to appear in the index list page, but it didn't work.

class BarAdmin(admin.ModelAdmin):
    def get_queryset(self, request):
        qs = super(BarAdmin, self).get_queryset(request)
        if request.user.is_superuser:
            return qs        
        return qs.filter(owner=request.user)

    def has_add_permission(self, request):
        return True

    def has_change_permission(self, request, obj=None):
        if obj is None:
            return True
        if obj.owner == request.user:
            return True
        return False

    def has_delete_permission(self, request, obj=None):
        if obj is None:
            return True
        if obj.owner == request.user:
            return True
        return False

    def has_module_permission(self, request):
        return True

Accessing the link admin/foo/bar/ works correctly for every user and returns the list of Bar instances for which obj.owner == request.user. admin/foo/bar/add allows the user to add a new object correctly. These links are although not displayed in the admin index page: which is the function that triggers the appearance of the model in the index page? admin/foo/ returns 403 Forbidden.


Uhm… this looks strange to me. So you don’t want to provide the user with add/change/delete permissions but you are faking them. 

The Bar model doesn’t appear in the App index list page because the view function in charge first verifies whether the user has any of the add/change/delete permissions granted for such App, and given that your user doesn’t have them the App Foo is not listed. In other words, you would have to override the index admin view too (in django.contrib.admin.sites.py), which I don’t recommend. Think that by overriding the way permissions are handled in the admin interface you might end up giving change access to regular users that shouldn’t have access at all. 

My recommendation here is to create your own supervising interface for Foo, with its own URLs, to provide the readonly functionality your target users needs. Those users might not probably fall in the category of admins. I’m thinking in maybe managers who need to see what’s going on but doesn’t have to have write access.

Does this answer your questions?

I'm using Django 1.7

Thanks,

Andrea



Cheers,
Daniel

signature.asc

Andrea

unread,
Oct 7, 2014, 9:30:03 AM10/7/14
to django...@googlegroups.com
Dear Daniel,

thank you for your answer.
I think I was not clear enough in explaining my problem. I will try to rephrase the problem, please tell me if from the first message this point was clear or seemed different.

I do want that the user is able to add or change some objects. My goal is to bypass the django admin permission system, and overriding "has_*_permission" methods seemed the correct option.

The following example is correlated but a bit simpler. Let's suppose I grant the access to all the users to the admin panel (is_staff=True for each user).

Now I want them to be able to add an instance of Bar model.

class BarAdmin(admin.ModelAdmin):

    def has_add_permission(self, request):
        return True
    def has_module_permission(self, request):
        return True
In my opinion that should do the trick, in fact the link to add a new instance is enabled, but it's not shown in the admin index page (and that's my problem).
What I do not want to do is to specifically assign the `foo.add_bar` permission under the `permissions` section in the user profile for each user.

Thanks for your time spent in tackling this issue.

Andrea

Andrea

unread,
Oct 7, 2014, 9:35:53 AM10/7/14
to django...@googlegroups.com
Dear Daniel,

I want to answer to an issue you raised in your previous message.

The Bar model doesn’t appear in the App index list page because the view function in charge first verifies whether the user has any of the add/change/delete permissions granted for such App, and given that your user doesn’t have them the App Foo is not listed.

This is the point I don't understand. I granted the user the add permission for the model, overriding the "has_add_permission" method for the admin model. In fact the add link can be manually reached and works properly. Which is the function in charge of checking for which models the user has permissions in the admin index page? I could override that one as well, as I did for the model with "has_add_permission".

Thanks,
Andrea

Daniel Rus Morales

unread,
Oct 7, 2014, 9:55:44 AM10/7/14
to django...@googlegroups.com
I think it’s clear to me what you are trying to do. By overriding those methods in BarAdmin you go down that line (bypass the django admin permission system), but to get the app listed in the admin index page you would have to actually rewrite the view function. This view function makes an explicit verification on the add/change/delete permissions for the logged in user and app Foo, and as long as your users don’t have them granted for such app they can’t see it listed.

Going back to my concern on overriding the permissions system, have you considered getting the functionality by providing your staff users with permissions at app Foo configuration time? That way your users would get access and you would write less code. You would create a group and would assign permissions to the group. At AppConfig.ready() you could assign your users to the new group, and thus grant them access.

Best,
Daniel


--
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users...@googlegroups.com.
To post to this group, send email to django...@googlegroups.com.
Visit this group at http://groups.google.com/group/django-users.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/CAAPQ7Y2DOSaYnScD68Ev-Mh%3D%2B6tH_GMrcKUrDOfsV76fX8OM_g%40mail.gmail.com.
For more options, visit https://groups.google.com/d/optout.

signature.asc

Andrea

unread,
Oct 7, 2014, 10:42:16 AM10/7/14
to django...@googlegroups.com
I think I've found the problem.

In my Django version I have in `contrib/admin/sites.py` line 371:

has_module_perms = user.has_module_perms(app_label)

Here it is instead:

https://github.com/django/django/blob/master/django/contrib/admin/sites.py
line 383:
has_module_perms = model_admin.has_module_permission(request)

I was probably reading the dev documentation of something that's not in Django 1.7 but will be in Django 1.8.

https://github.com/django/django/commit/504c89e8008c557a1e83c45535b549f77a3503b2

Thanks!
Andrea
Reply all
Reply to author
Forward
0 new messages