首页 > 解决方案 > 在 django orm 中使用多字段进行过滤

问题描述

我有一些字段可以用来过滤列表。我可以在过滤查询中使用所有这些字段或其中一些字段。我的观点如下。但我无法得到正确的答案。这意味着我期望被选中的一些记录没有被选中。

class UserListAPIView(ListAPIView):

serializer_class = serializers.UserListSerializer
pagination_class = AdminPagination
permission_classes = (IsAuthRolePermission,)

filter_backends = (filters.OrderingFilter,)
ordering_fields = '__all__'


def get_queryset(self):

    full_name     = self.request.query_params.get('full_name', '')
    email         = self.request.query_params.get('email', '')
    facility_name = self.request.query_params.get('facility_name', '')
    user_state    = self.request.query_params.get('user_state', '')

    if full_name or email or facility_name or user_state:

        queryset = User.objects.filter(full_name__icontains=full_name,
                                       email__icontains=email,
                                       facility_name__icontains=facility_name,
                                       user_state__icontains=user_state) \
            .annotate(facility=F('facility_name'),
                      state=F('user_state'),
                      last_access=F('lastAccess')) \
            .order_by('-id').distinct()

    else:
        queryset = User.objects.all()\
            .annotate(facility=F('facility_name'),
                      state=F('user_state'),
                      last_access=F('lastAccess'))\
            .order_by('-id')

    return queryset

标签: djangodjango-rest-framework

解决方案


你做错了。首先,让我建议这种过滤应该在filter_queryset方法中完成,而不是get_queryset方法或更好,使用django-filter 中的 DjangoFilterBackend这是常见的做法。

Now pertaining your code, you add the filters even when they are not specified. Assuming the client has added the query param full_name=foo, your code goes on to add email="",facility_name="",user_state="" to the filter which will most likely not return anything as there will be no combination of those values in the database. So rather, than doing that, add only the query params specified.

Something like this:

def get_queryset(self):

    filter_fields = ['full_name', 'email', 'facility_name', 'user_state']

    params = {'{}__icontains'.format(k): v for k,v in self.request.query_params.items() if k in filter_fields}

    queryset = User.objects.filter(**params) \
                .annotate(facility=F('facility_name'),
                          state=F('user_state'),
                          last_access=F('lastAccess')) \
                .order_by('-id').distinct()

    return queryset

Again, you should consider moving this to filter_queryset as that is the method meant for filtering.


推荐阅读