Skip to content

Commit de3b2a0

Browse files
authored
Fix filter caching to work with all data types (#134)
* only cache if lambda result is a django model * refactor filter caching * PR feedback and bumpversion
1 parent e7694ff commit de3b2a0

File tree

5 files changed

+39
-18
lines changed

5 files changed

+39
-18
lines changed

.bumpversion.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[bumpversion]
2-
current_version = 0.30.0
2+
current_version = 0.31.0
33

44
[bumpversion:file:pyproject.toml]
55

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
55
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

7+
# [0.31.0 = 2023-09-11]
8+
- [PR 134](https://github.com/salesforce/django-declarative-apis/pull/134) Make filter caching work for all datatypes
9+
710
# [0.30.0] - 2023-08-23
811
- [PR 131](https://github.com/salesforce/django-declarative-apis/pull/131) Support non-utf8 request body
912

django_declarative_apis/machinery/filtering.py

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -76,26 +76,41 @@ def _is_reverse_relation(field):
7676
return isinstance(field, ForeignObjectRel)
7777

7878

79-
def _cache_related_instance(inst, field_name, model_cache):
79+
def _get_callable_field_value_with_cache(inst, field_name, model_cache, field_type):
80+
cache_relation = True
81+
8082
try:
8183
field_meta = inst._meta.get_field(field_name)
84+
if not _is_relation(field_meta) or _is_reverse_relation(field_meta):
85+
# no `attname` in reverse relations
86+
cache_relation = False
8287
except (AttributeError, FieldDoesNotExist):
83-
return
84-
if not _is_relation(field_meta) or _is_reverse_relation(field_meta):
85-
return # no `attname` in reverse relations
86-
val_pk = getattr(inst, field_meta.attname)
87-
val_cls = field_meta.related_model
88-
cache_key = _make_model_cache_key(val_cls, val_pk)
88+
# inst doesn't look like a django model
89+
cache_relation = False
90+
91+
if cache_relation:
92+
# we're caching a foreign key field on a django model. Cache it by (model, fk_pk) so that if
93+
# other objects reference this same instance, we'll get a cache hit
94+
fk_pk = getattr(inst, field_meta.attname)
95+
val_cls = field_meta.related_model
96+
cache_key = _make_model_cache_key(val_cls, fk_pk)
97+
else:
98+
# not a foreign key. Cache it by (inst, field_name) - it won't be a cache hit on another instance, but
99+
# will be cached if this same inst is returned later in the response
100+
cache_key = (inst, field_name)
101+
89102
if cache_key in model_cache:
90103
logger.debug("ev=model_cache, status=hit, key=%s", cache_key)
91-
related_instance = model_cache[cache_key]
104+
result = model_cache[cache_key]
92105
else:
93106
logger.debug("ev=model_cache, status=miss, key=%s", cache_key)
94-
related_instance = getattr(inst, field_name)
95-
if not isinstance(related_instance, val_cls):
96-
return
97-
model_cache[cache_key] = related_instance
98-
setattr(inst, field_name, related_instance)
107+
result = field_type(inst)
108+
109+
if isinstance(result, models.Manager):
110+
# need to get an iterable to proceed
111+
result = result.all()
112+
model_cache[cache_key] = result
113+
return result
99114

100115

101116
def _get_filtered_field_value( # noqa: C901
@@ -114,8 +129,11 @@ def _get_filtered_field_value( # noqa: C901
114129

115130
if isinstance(field_type, types.FunctionType):
116131
if is_caching_enabled():
117-
_cache_related_instance(inst, field_name, model_cache)
118-
val = field_type(inst)
132+
val = _get_callable_field_value_with_cache(
133+
inst, field_name, model_cache, field_type
134+
)
135+
else:
136+
val = field_type(inst)
119137
elif isinstance(field_type, _ExpandableForeignKey):
120138
if expand_this:
121139
inst_field_name = field_type.inst_field_name or field_name

docs/source/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@
7474
# built documents.
7575

7676
# The full version, including alpha/beta/rc tags.
77-
release = '0.30.0' # set by bumpversion
77+
release = '0.31.0' # set by bumpversion
7878

7979
# The short X.Y version.
8080
version = release.rsplit('.', 1)[0]

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "django-declarative-apis"
7-
version = "0.30.0" # set by bumpversion
7+
version = "0.31.0" # set by bumpversion
88
description = "Simple, readable, declarative APIs for Django"
99
readme = "README.md"
1010
dependencies = [

0 commit comments

Comments
 (0)