简体   繁体   中英

Django - How to access filtered queryset in the dispatch method?

I have the following code:

class MyViewSet(ModelViewSet):
    ...
    filter_backends = (...)

    def dispatch(self, request, *args, **kwargs):
        response = super(MyViewSet, self).dispatch(
            request,
            *args,
            **kwargs
        )
        ... # do something with the response
        return response

Inside the dispatch method, I can retrieve the filtered data with response.data , so I assume the custom filter backend is working properly. However, I also want to do something with the queryset as well (eg call count() ), after the filter has been applied to it. The problem is that self.queryset and self.get_queryset() return the whole, non-filtered queryset.

So how do get the version of queryset to which the filter has been applied in the dispatch method, if it's possible at all?

You could use the filter_queryset method. It will filter it with whichever filter backend is in use. See its definition in GenericAPIView class.

def filter_queryset(self, queryset):
    """
    Given a queryset, filter it with whichever filter backend is in use.
    You are unlikely to want to override this method, although you may need
    to call it either from a list view, or from a custom `get_object`
    method if you want to apply the configured filtering backend to the
    default queryset.
    """
    for backend in list(self.filter_backends):
        queryset = backend().filter_queryset(self.request, queryset, self)
    return queryset

So you can filter the queryset like,

filtered_queryset = self.filter_queryset(self.get_queryset())
# Then do something with the filtered queryset

Note that self.filter_queryset(self.get_queryset()) will filter the queryset again even if the queryset was already filtered during the following super call

super(MyViewSet, self).filter_queryset(
                                self.queryset()
                                ).

Following is one way to overcome the duplicate filter call. You could override the filter_queryset method and attach the filtered queryset to the instance.( please note that this solution is not tested )

ie, something like this.

class MyViewSet(ModelViewSet):
    ...
    filter_backends = (...)

    def dispatch(self, request, *args, **kwargs):
        response = super(MyViewSet, self).dispatch(
            request,
            *args,
            **kwargs
        )
        ... # do something with the response
        # After this super call `_filtered_query_set` attribute will be set.
        # so use `self._filtered_query_set` wherever needed
        return response

   def filter_queryset(self, queryset):
       filtered_query_set = super(MyViewSet, self).filter_queryset(
                            self.queryset()
                            )
       self._filtered_query_set = filtered_query_set
       return filtered_query_set

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM