Django "list_filter" based on the admin queryset

时间:2019-04-08 13:14:52

标签: python django

I am developing an application which has both admins and sub-admins. Sub-admins are created based on the group they belong to. So a sub-admin can see only data which is related to his group. This functionality is working perfectly.

Now, I want to create a filter option based on users. For super-admin it works fine but when I see from sub-admin, I can see the complete list of users in the filter list. How can I limit this list based on sub-admins users.

Suppose there is a total of 20 users, and sub-admin has only 3 users in his group. So I am able to see only 3 in the list view but in the filter option I can see all 20. Can I limit this filter to only those 3 users only?

My admin model looks like below:

class VideoDetailsAdmin(admin.ModelAdmin):
    list_display = ('video_id', 'user', 'status', 'description', 'video_name', 'upload_time', 'duration')

    list_filter = ('user', )

    def get_queryset(self, request):
        # Get the groups of logged in user
        query_set = Group.objects.filter(user = request.user)
        group_list = []
        for g in query_set:
            group_list.append(g.name)
        # Get the user ids of Users in those groups
        id_list = []
        for user in list(User.objects.filter(groups__name__in=group_list)):
            id_list.append(user.id)
        # Create unique users list
        users = list(set(id_list))
        # Override the get_queryset method for Admin
        qs = super(VideoDetailsAdmin, self).get_queryset(request)

        if not request.user.is_superuser:
            return qs.filter(user__in = users)
        else:
            return qs

I've seen few resources available on Django documentation, but I am wondering what could be the best approach to solve this problem. Is there a way I can re-use the get_queryset() code

Django Version: 2.1

EDIT 1:

Made following modifications but no filter is visible.

# Filter list
class UserFilterList(admin.SimpleListFilter):
    # Human readable title, which is displayed on the right sidebar
    title = ("User")

    # Parameter for the filter that will be used in the URL query
    parameter_name = "user"

    def lookups(self, request, model_admin):
        # To get user's groups
        query_set = Group.objects.filter(user = request.user)
        group_list = []
        for g in query_set:
            group_list.append(g.name)
        # To get all users associated in those groups
        id_list = []
        for user in list(User.objects.filter(groups__name__in=group_list)):
            id_list.append(user.id)

        users = list(set(id_list))
        qs = model_admin.get_queryset(request)

    def queryset(self, request, queryset):
        if not request.user.is_superuser:
            return qs.filter(user__in = users)
        else:
            return qs

EDIT 2:

class UserFilterList(SimpleListFilter):
    title = "user"
    parameter_name = "user"
    def lookups(self, request, model_admin):
        visible_users = model_admin.get_visible_users(request)
        print(visible_users[0])
        return ((user, user) for user in visible_users)

    def queryset(self, request, queryset):
        return self.value()

FINAL WORKING SOLUTION (THANKS TO @dirkgroten):

class UserFilterList(SimpleListFilter):
    title = "user"
    parameter_name = "user"
    def lookups(self, request, model_admin):
        if not request.user.is_superuser:
            visible_users = model_admin.get_visible_users(request)
            # Sub user - return same group users
            return ((user.id, user) for user in visible_users)
        else:
            # Superuser - return all users
            return ((user.id, user) for user in User.objects.filter())

    def queryset(self, request, queryset):
        return queryset.filter(user=self.value()) if self.value() else queryset

class VideoDetailsAdmin(admin.ModelAdmin):
    list_display = ('video_id', 'user', 'status', 'description', 'video_name', 'upload_time', 'duration')

    list_filter = (UserFilterList, )

    def get_visible_users(self, request):
        # Get the groups of logged in user
        query_set = Group.objects.filter(user = request.user)
        group_list = []
        for g in query_set:
            group_list.append(g.name)
        return User.objects.filter(groups__name__in=group_list)

    def get_queryset(self, request):
        users = self.get_visible_users(request)

        # Override the get_queryset method for Admin
        qs = super(VideoDetailsAdmin, self).get_queryset(request)

        if not request.user.is_superuser:
            return qs.filter(user__in = users)
        else:
            return qs

2 个答案:

答案 0 :(得分:1)

If I understood your question correctly, you should override the get_list_filter() method instead of get_queryset() method,

class VideoDetailsAdmin(admin.ModelAdmin):
    list_display = ('video_id', 'user', 'status', 'description', 'video_name', 'upload_time', 'duration')

    list_filter = ('user',)

    def get_list_filter(self, request):
        if request.user in [your_three_user_list]:
            return self.list_filter
        else:
            return ()

答案 1 :(得分:1)

请勿更改您的VideoDetailsAdmin,仅使用自定义列表过滤器:

class VideoDetailsAdmin(ModelAdmin):
    list_filter = UserFilterList  # that's the only line to change

    def get_visible_users(self, request):  # small refactor to re-use in filter
        query_set = Group.objects.filter(user=request.user)
        group_list = []
        for g in query_set:
            group_list.append(g.name)
        # To get all users associated in those groups
        return User.objects.filter(groups__name__in=group_list)

    def get_queryset(self, request):
        users = self.get_visible_users(request)
        # Override the get_queryset method for Admin
        qs = super(VideoDetailsAdmin, self).get_queryset(request)

        if not request.user.is_superuser:
            return qs.filter(user__in=users)
        else:
            return qs

class UserFilterList(SimpleListFilter):
    def lookups(self, request, model_admin):
        visible_users = model_admin.get_visible_users(request)
        return ((user.pk, user.username) for user in visible_users)

    def queryset(self, request, queryset):
        return queryset.filter(user_id=self.value()) if self.value() else queryset