Skip to content

Commit 3475f9d

Browse files
authored
fix:filter caching for unhashable types (#135)
* add test for unhashable obj filter caching * hash instances by id() instead of the object itself * changelog and bumpversion
1 parent 1334779 commit 3475f9d

8 files changed

Lines changed: 40 additions & 7 deletions

File tree

.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.31.1
2+
current_version = 0.31.2
33

44
[bumpversion:file:pyproject.toml]
55

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ 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.1 = 2023-09-11]
7+
# [0.31.2 = 2023-09-11]
88
- [PR 134](https://github.com/salesforce/django-declarative-apis/pull/134) Make filter caching work for all datatypes
99
- [PR 131](https://github.com/salesforce/django-declarative-apis/pull/131) Support non-utf8 request body
1010

django_declarative_apis/machinery/filtering.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ def _get_callable_field_value_with_cache(inst, field_name, model_cache, field_ty
9797
else:
9898
# not a foreign key. Cache it by (inst, field_name) - it won't be a cache hit on another instance, but
9999
# will be cached if this same inst is returned later in the response
100-
cache_key = (inst, field_name)
100+
cache_key = (id(inst), field_name)
101101

102102
if cache_key in model_cache:
103103
logger.debug("ev=model_cache, status=hit, key=%s", cache_key)

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.31.1' # set by bumpversion
77+
release = '0.31.2' # 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.31.1" # set by bumpversion
7+
version = "0.31.2" # set by bumpversion
88
description = "Simple, readable, declarative APIs for Django"
99
readme = "README.md"
1010
dependencies = [

tests/filters.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
# For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
66
#
77

8-
from django_declarative_apis.machinery.filtering import ALWAYS, expandable
8+
from django_declarative_apis.machinery.filtering import ALWAYS, NEVER, expandable
99

1010
from . import models
1111

@@ -50,6 +50,12 @@
5050
models.InefficientBranchA: {"leaf": lambda inst: inst.leaf},
5151
models.InefficientBranchB: {"leaf": lambda inst: inst.leaf},
5252
models.InefficientRoot: {"branch_a": ALWAYS, "branch_b": ALWAYS},
53+
models.InefficientPydanticRoot: {
54+
"default_factory": NEVER,
55+
"__len__": NEVER,
56+
"branch_a": lambda inst: inst.branch_a,
57+
"branch_b": lambda inst: inst.branch_b,
58+
},
5359
}
5460

5561
RENAMED_EXPANDABLE_MODEL_FIELDS = {

tests/machinery/test_base.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -761,6 +761,27 @@ def _mark_as_fake_relation(self, field):
761761
field.is_fake_relation = True
762762

763763

764+
class FilterCachingUnhashableTestCase(django.test.TestCase):
765+
def setUp(self):
766+
leaf = models.InefficientLeaf.objects.create(id=1)
767+
branch_a = models.InefficientBranchA.objects.create(id=1, leaf=leaf)
768+
branch_b = models.InefficientBranchB.objects.create(id=1, leaf=leaf)
769+
self.root = models.InefficientPydanticRoot(
770+
id=4, branch_a=branch_a, branch_b=branch_b
771+
)
772+
773+
def test_filtering_accepts_nonhashable_root_item(self):
774+
with override_settings(
775+
DDA_FILTER_MODEL_CACHING_ENABLED=True, DDA_FILTER_CACHE_DEBUG_LOG=True
776+
):
777+
try:
778+
filtering.apply_filters_to_object(
779+
self.root, filters.INEFFICIENT_FUNCTION_FILTERS
780+
)
781+
except TypeError:
782+
self.fail("should be able to use filter caching on pydantic objects")
783+
784+
764785
class ResourceUpdateEndpointDefinitionTestCase(
765786
testutils.RequestCreatorMixin, django.test.TestCase
766787
):

tests/models.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
# SPDX-License-Identifier: BSD-3-Clause
55
# For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
66
#
7-
7+
import pydantic
88
from django.db import models
99

1010

@@ -73,6 +73,12 @@ class InefficientRoot(models.Model):
7373
branch_b = models.ForeignKey(InefficientBranchB, on_delete=models.CASCADE)
7474

7575

76+
class InefficientPydanticRoot(pydantic.BaseModel, arbitrary_types_allowed=True):
77+
id: int = None
78+
branch_a: InefficientBranchA = None
79+
branch_b: InefficientBranchB = None
80+
81+
7682
try:
7783
import dirtyfields
7884

0 commit comments

Comments
 (0)