7
7
from rest_framework .filters import BaseFilterBackend
8
8
9
9
10
- class FilterCache :
10
+ class _FilterClassCache :
11
11
CACHE = {}
12
12
13
13
@classmethod
@@ -25,16 +25,42 @@ class ViewSet(mixins.ListModelMixin, GenericViewSet):
25
25
"""
26
26
OPENAPI_RETRIEVE_SPECIFICATION = False
27
27
28
+ _CACHES = {}
29
+
28
30
def filter_queryset (self , request , queryset , view ):
29
31
filter_class = self .get_filter_class (view )
30
32
if not filter_class :
31
33
return queryset
32
34
33
35
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 ()
38
64
39
65
def get_schema_operation_parameters (self , view ):
40
66
spec = []
@@ -59,14 +85,25 @@ def get_filter_class(view):
59
85
def get_query (cls , filter_instance , request , view ):
60
86
return get_query (request )
61
87
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
+ )
65
94
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 )
67
100
if filter_instance :
68
101
return filter_class (queryset = queryset , instance = filter_instance )
69
102
70
103
filter_instance = filter_class (queryset )
71
- FilterCache .CACHE [qual_name ] = filter_instance
104
+ _FilterClassCache .CACHE [qual_name ] = filter_instance
72
105
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