Skip to content

Commit 05dde9e

Browse files
committed
Disabled FilterCls cache by default and fixed cache related bugs
1 parent f2ace62 commit 05dde9e

File tree

4 files changed

+30
-27
lines changed

4 files changed

+30
-27
lines changed

dj_rql/drf/backend.py

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -35,29 +35,32 @@ def filter_queryset(self, request, queryset, view):
3535
filter_instance = self._get_filter_instance(filter_class, queryset, view)
3636
query = self.get_query(filter_instance, request, view)
3737

38-
def apply_filters_lazy():
39-
return filter_instance.apply_filters(query, request, view)
40-
41-
if filter_class.QUERIES_CACHE_BACKEND and request.method in ('GET', 'HEAD', 'OPTIONS'):
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:
4244
# We must use the combination of queryset and query to make a cache key as
4345
# queryset can already contain some filters (e.x. based on authentication)
44-
cache_key = hash(str(queryset.query) + query)
46+
cache_key = str(queryset.query) + query
4547

4648
query_cache = self._get_or_init_cache(filter_class, view)
4749
filters_result = query_cache.get(cache_key)
4850
if not filters_result:
49-
filters_result = apply_filters_lazy()
51+
filters_result = filter_instance.apply_filters(query, request, view)
5052
query_cache[cache_key] = filters_result
53+
5154
else:
52-
filters_result = apply_filters_lazy()
55+
filters_result = filter_instance.apply_filters(query, request, view)
5356

5457
rql_ast, queryset = filters_result
5558

5659
request.rql_ast = rql_ast
5760
if queryset.select_data:
5861
request.rql_select = queryset.select_data
5962

60-
return queryset
63+
return queryset.all()
6164

6265
def get_schema_operation_parameters(self, view):
6366
spec = []
@@ -84,14 +87,14 @@ def get_query(cls, filter_instance, request, view):
8487

8588
@classmethod
8689
def _get_or_init_cache(cls, filter_class, view):
87-
qual_name = cls._get_filter_cls_qual_name(filter_class, view)
90+
qual_name = cls._get_filter_cls_qual_name(view)
8891
return cls._CACHES.setdefault(
8992
qual_name, filter_class.QUERIES_CACHE_BACKEND(int(filter_class.QUERIES_CACHE_SIZE)),
9093
)
9194

9295
@classmethod
9396
def _get_filter_instance(cls, filter_class, queryset, view):
94-
qual_name = cls._get_filter_cls_qual_name(filter_class, view)
97+
qual_name = cls._get_filter_cls_qual_name(view)
9598

9699
filter_instance = _FilterClassCache.CACHE.get(qual_name)
97100
if filter_instance:
@@ -102,5 +105,5 @@ def _get_filter_instance(cls, filter_class, queryset, view):
102105
return filter_instance
103106

104107
@staticmethod
105-
def _get_filter_cls_qual_name(filter_class, view):
106-
return '{0}.{1}'.format(view.basename, filter_class.__name__)
108+
def _get_filter_cls_qual_name(view):
109+
return '{0}.{1}'.format(view.__class__.__module__, view.__class__.__name__)

dj_rql/filter_cls.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@
66
from datetime import datetime
77
from uuid import uuid4
88

9-
from cachetools import LFUCache
10-
119
from dj_rql._dataclasses import FilterArgs, OptimizationArgs
1210
from dj_rql.constants import (
1311
ComparisonOperators,
@@ -64,8 +62,8 @@ class RQLFilterClass:
6462
OPENAPI_SPECIFICATION = RQLFilterClassSpecification
6563
"""Class for OpenAPI specifications generation."""
6664

67-
QUERIES_CACHE_BACKEND = LFUCache
68-
"""Class for query caching (can be `None`)."""
65+
QUERIES_CACHE_BACKEND = None
66+
"""Class for query caching."""
6967

7068
QUERIES_CACHE_SIZE = 20
7169
"""Default number of cached queries."""

tests/dj_rf/filters.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#
22
# Copyright © 2021 Ingram Micro Inc. All rights reserved.
33
#
4-
from cachetools import LRUCache
4+
from cachetools import LFUCache, LRUCache
55

66
from dj_rql.constants import FilterLookups, RQL_NULL
77
from dj_rql.drf.fields import SelectField
@@ -35,6 +35,7 @@
3535

3636
class BooksFilterClass(RQLFilterClass):
3737
MODEL = Book
38+
QUERIES_CACHE_BACKEND = LFUCache
3839
FILTERS = ['id', {
3940
'filter': 'title',
4041
'null_values': {RQL_NULL, 'NULL_ID'},

tests/test_drf/test_common_drf_backend.py

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ def test_filter_cls_cache(api_client, clear_cache):
103103
response = api_client.get('{0}?{1}'.format(reverse('book-list'), 'title=F'))
104104
assert response.data == [{'id': books[0].pk}]
105105

106-
expected_cache_key = 'book.BooksFilterClass'
106+
expected_cache_key = 'tests.dj_rf.view.DRFViewSet'
107107
assert expected_cache_key in _FilterClassCache.CACHE
108108
cache_item_id = id(_FilterClassCache.CACHE[expected_cache_key])
109109

@@ -118,15 +118,16 @@ def test_filter_cls_cache(api_client, clear_cache):
118118

119119

120120
@pytest.mark.django_db
121-
def test_query_cache(api_client, clear_cache):
121+
def test_query_cache(api_client, clear_cache, django_assert_num_queries):
122122
books = [
123123
Book.objects.create(title='F'),
124124
Book.objects.create(title='G'),
125125
]
126126

127127
for _ in range(4):
128-
response = api_client.get('{0}?{1}'.format(reverse('book-list'), 'title=F'))
129-
assert response.data == [{'id': books[0].pk}]
128+
with django_assert_num_queries(2):
129+
response = api_client.get('{0}?{1}'.format(reverse('book-list'), 'title=F'))
130+
assert response.data == [{'id': books[0].pk}]
130131

131132
response = api_client.get('{0}?{1}'.format(reverse('book-list'), 'title=X'))
132133
assert response.data == []
@@ -136,12 +137,12 @@ def test_query_cache(api_client, clear_cache):
136137
assert 'id' not in response.data[0]
137138

138139
caches = RQLFilterBackend._CACHES
139-
assert isinstance(caches['book.BooksFilterClass'], LFUCache)
140-
assert caches['book.BooksFilterClass'].currsize == 2
141-
assert caches['book.BooksFilterClass'].maxsize == 20
142-
assert isinstance(caches['select.SelectBooksFilterClass'], LRUCache)
143-
assert caches['select.SelectBooksFilterClass'].currsize == 1
144-
assert caches['select.SelectBooksFilterClass'].maxsize == 100
140+
assert isinstance(caches['tests.dj_rf.view.DRFViewSet'], LFUCache)
141+
assert caches['tests.dj_rf.view.DRFViewSet'].currsize == 2
142+
assert caches['tests.dj_rf.view.DRFViewSet'].maxsize == 20
143+
assert isinstance(caches['tests.dj_rf.view.SelectViewSet'], LRUCache)
144+
assert caches['tests.dj_rf.view.SelectViewSet'].currsize == 1
145+
assert caches['tests.dj_rf.view.SelectViewSet'].maxsize == 100
145146

146147

147148
@pytest.mark.django_db

0 commit comments

Comments
 (0)