77from rest_framework .filters import BaseFilterBackend
88
99
10- class FilterCache :
10+ class _FilterClassCache :
1111 CACHE = {}
1212
1313 @classmethod
@@ -25,16 +25,42 @@ class ViewSet(mixins.ListModelMixin, GenericViewSet):
2525 """
2626 OPENAPI_RETRIEVE_SPECIFICATION = False
2727
28+ _CACHES = {}
29+
2830 def filter_queryset (self , request , queryset , view ):
2931 filter_class = self .get_filter_class (view )
3032 if not filter_class :
3133 return queryset
3234
3335 filter_instance = self ._get_filter_instance (filter_class , queryset , view )
34- rql_ast , queryset = filter_instance .apply_filters (
35- self .get_query (filter_instance , request , view ), request , view ,
36- )
37- return queryset
36+ query = self .get_query (filter_instance , request , view )
37+
38+ can_query_be_cached = all ((
39+ filter_class .QUERIES_CACHE_BACKEND ,
40+ filter_class .QUERIES_CACHE_SIZE ,
41+ request .method in ('GET' , 'HEAD' , 'OPTIONS' ),
42+ ))
43+ if can_query_be_cached :
44+ # We must use the combination of queryset and query to make a cache key as
45+ # queryset can already contain some filters (e.x. based on authentication)
46+ cache_key = str (queryset .query ) + query
47+
48+ query_cache = self ._get_or_init_cache (filter_class , view )
49+ filters_result = query_cache .get (cache_key )
50+ if not filters_result :
51+ filters_result = filter_instance .apply_filters (query , request , view )
52+ query_cache [cache_key ] = filters_result
53+
54+ else :
55+ filters_result = filter_instance .apply_filters (query , request , view )
56+
57+ rql_ast , queryset = filters_result
58+
59+ request .rql_ast = rql_ast
60+ if queryset .select_data :
61+ request .rql_select = queryset .select_data
62+
63+ return queryset .all ()
3864
3965 def get_schema_operation_parameters (self , view ):
4066 spec = []
@@ -59,14 +85,25 @@ def get_filter_class(view):
5985 def get_query (cls , filter_instance , request , view ):
6086 return get_query (request )
6187
62- @staticmethod
63- def _get_filter_instance (filter_class , queryset , view ):
64- qual_name = '{0}.{1}' .format (view .basename , filter_class .__name__ )
88+ @classmethod
89+ def _get_or_init_cache (cls , filter_class , view ):
90+ qual_name = cls ._get_filter_cls_qual_name (view )
91+ return cls ._CACHES .setdefault (
92+ qual_name , filter_class .QUERIES_CACHE_BACKEND (int (filter_class .QUERIES_CACHE_SIZE )),
93+ )
6594
66- filter_instance = FilterCache .CACHE .get (qual_name )
95+ @classmethod
96+ def _get_filter_instance (cls , filter_class , queryset , view ):
97+ qual_name = cls ._get_filter_cls_qual_name (view )
98+
99+ filter_instance = _FilterClassCache .CACHE .get (qual_name )
67100 if filter_instance :
68101 return filter_class (queryset = queryset , instance = filter_instance )
69102
70103 filter_instance = filter_class (queryset )
71- FilterCache .CACHE [qual_name ] = filter_instance
104+ _FilterClassCache .CACHE [qual_name ] = filter_instance
72105 return filter_instance
106+
107+ @staticmethod
108+ def _get_filter_cls_qual_name (view ):
109+ return '{0}.{1}' .format (view .__class__ .__module__ , view .__class__ .__name__ )
0 commit comments