Skip to content

Commit 08bc20d

Browse files
authored
add ExpandableGeneric filter type (#146)
* add ExpandableGeneric * changelog and bumpversion * populate __expandable__ correctly * fix test * fix typo
1 parent 8c3dbc2 commit 08bc20d

File tree

8 files changed

+71
-11
lines changed

8 files changed

+71
-11
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.31.5
2+
current_version = 0.31.6
33

44
[bumpversion:file:pyproject.toml]
55

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77
# [Unreleased]
88
- [PR 144](https://github.com/salesforce/django-declarative-apis/pull/141) Allow Dependabot to update dev dependencies
99
- [PR 143](https://github.com/salesforce/django-declarative-apis/pull/143) Testing: Replace `flake8` and `black` with `ruff`, add testing for Python 3.12, drop testing for Python 3.7
10+
- [PR 146](https://github.com/salesforce/django-declarative-apis/pull/146) Add ExpandableGeneric filter type
1011

1112
# [0.31.5]
1213
- [PR 141](https://github.com/salesforce/django-declarative-apis/pull/141) Clean up logging

django_declarative_apis/machinery/filtering.py

Lines changed: 18 additions & 2 deletions
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+
from abc import ABC, abstractmethod
88
from collections import defaultdict
99
import inspect
1010
import logging
@@ -45,6 +45,16 @@ def expandable(model_class=None, display_key=None, inst_field_name=None):
4545
return _ExpandableForeignKey(display_key, model_class, inst_field_name)
4646

4747

48+
class ExpandableGeneric(ABC):
49+
@abstractmethod
50+
def get_unexpanded_view(self, inst) -> dict:
51+
raise NotImplementedError()
52+
53+
@abstractmethod
54+
def get_expanded_view(self, inst) -> dict:
55+
raise NotImplementedError()
56+
57+
4858
def _get_unexpanded_field_value(inst, field_name, field_type):
4959
if not field_type.model_class:
5060
return DEFAULT_UNEXPANDED_VALUE
@@ -141,6 +151,11 @@ def _get_filtered_field_value( # noqa: C901
141151
val = getattr(inst, inst_field_name)
142152
else:
143153
val = _get_unexpanded_field_value(inst, field_name, field_type)
154+
elif isinstance(field_type, ExpandableGeneric):
155+
if expand_this:
156+
val = field_type.get_expanded_view(inst)
157+
else:
158+
val = field_type.get_unexpanded_view(inst)
144159
else:
145160
try:
146161
if isinstance(inst, (models.Model)):
@@ -184,6 +199,7 @@ def _get_filtered_field_value( # noqa: C901
184199
or isinstance(field_type, types.FunctionType)
185200
or ((field_type == IF_TRUTHY) and val)
186201
or isinstance(field_type, _ExpandableForeignKey)
202+
or isinstance(field_type, ExpandableGeneric)
187203
):
188204
return val
189205
else:
@@ -280,7 +296,7 @@ def _apply_filters_to_object( # noqa: C901
280296
return inst
281297
else:
282298
for field_name, field_type in fields_def.items():
283-
if isinstance(field_type, _ExpandableForeignKey):
299+
if isinstance(field_type, (_ExpandableForeignKey, ExpandableGeneric)):
284300
expandables.append(field_name)
285301

286302
value = _get_filtered_field_value(

docs/source/conf.py

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

7777
# The full version, including alpha/beta/rc tags.
78-
release = "0.31.5" # set by bumpversion
78+
release = "0.31.6" # set by bumpversion
7979

8080
# The short X.Y version.
8181
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.5" # set by bumpversion
7+
version = "0.31.6" # set by bumpversion
88
description = "Simple, readable, declarative APIs for Django"
99
readme = "README.md"
1010
dependencies = [

tests/filters.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,24 @@
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, NEVER, expandable
8+
from django_declarative_apis.machinery.filtering import (
9+
ALWAYS,
10+
NEVER,
11+
expandable,
12+
ExpandableGeneric,
13+
)
914

1015
from . import models
1116

17+
18+
class TestExpandableGeneric(ExpandableGeneric):
19+
def get_unexpanded_view(self, inst) -> dict:
20+
return {"id": "1234"}
21+
22+
def get_expanded_view(self, inst) -> dict:
23+
return {"id": "1234", "expanded": True}
24+
25+
1226
DEFAULT_FILTERS = {
1327
str: ALWAYS,
1428
int: ALWAYS,
@@ -18,6 +32,7 @@
1832
"int_field": ALWAYS,
1933
"expandable_dict": expandable(),
2034
"expandable_string": expandable(),
35+
"expandable_generic": TestExpandableGeneric(),
2136
},
2237
models.ChildModel: {
2338
"pk": ALWAYS,

tests/test_filters.py

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,23 @@ def setUp(self):
3131
self.p1.root = self.root
3232
self.p1.save()
3333

34+
def test_expandable_generic_field(self):
35+
# test unexpanded
36+
filtered = filtering.apply_filters_to_object(
37+
self.test_model, filters.DEFAULT_FILTERS
38+
)
39+
self.assertIn("expandable_generic", filtered)
40+
self.assertEqual("1234", filtered["expandable_generic"]["id"])
41+
self.assertNotIn("expanded", filtered["expandable_generic"])
42+
43+
# test expanded
44+
filtered = filtering.apply_filters_to_object(
45+
self.test_model, filters.DEFAULT_FILTERS, expand_header="expandable_generic"
46+
)
47+
self.assertIn("expandable_generic", filtered)
48+
self.assertEqual("1234", filtered["expandable_generic"]["id"])
49+
self.assertTrue(filtered["expandable_generic"]["expanded"])
50+
3451
def test_expandable_field_not_expanded_by_default(self):
3552
filtered = filtering.apply_filters_to_object(self.root, filters.DEFAULT_FILTERS)
3653
self.assertEqual(4, len(filtered))
@@ -115,7 +132,7 @@ def test_expand_multi_level_more_than_one_field(self):
115132
self.assertTrue("pk" in filtered["parent_field"]["favorite"])
116133
self.assertTrue("name" in filtered["parent_field"]["favorite"])
117134
self.assertTrue("test" in filtered["parent_field"]["favorite"])
118-
self.assertEqual(3, len(filtered["parent_field"]["favorite"]["test"]))
135+
self.assertEqual(4, len(filtered["parent_field"]["favorite"]["test"]))
119136
self.assertTrue("parent" in filtered["parent_field"]["favorite"])
120137
self.assertEqual(self.p1c1.name, filtered["parent_field"]["favorite"]["name"])
121138
self.assertEqual(2, len(filtered["parent_field"]["children"]))
@@ -135,7 +152,7 @@ def test_expandable_properties(self):
135152
expand_header="expandable_dict,expandable_string",
136153
)
137154

138-
self.assertEqual(5, len(filtered))
155+
self.assertEqual(6, len(filtered))
139156
self.assertTrue("expandable_dict" in filtered)
140157
self.assertEqual(
141158
filtered["expandable_dict"], models.TestModel.EXPANDABLE_DICT_RETURN
@@ -157,14 +174,15 @@ def test_expandable_properties(self):
157174
self.test_model, filters.DEFAULT_FILTERS
158175
)
159176

160-
self.assertEqual(3, len(filtered))
177+
self.assertEqual(4, len(filtered))
161178
self.assertFalse("expandable_dict" in filtered)
162179
dict_mock.assert_not_called()
163180
self.assertFalse("expandable_string" in filtered)
164181
str_mock.assert_not_called()
165182
self.assertTrue("__expandable__" in filtered)
166183
self.assertTrue("expandable_dict" in filtered["__expandable__"])
167184
self.assertTrue("expandable_string" in filtered["__expandable__"])
185+
self.assertTrue("expandable_generic" in filtered["__expandable__"])
168186

169187
def test_expandable_absent_if_no_expandable_fields(self):
170188
filtered = filtering.apply_filters_to_object(

tests/tests.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,23 @@ def test_dict_endpoint(self):
3939
"test": {
4040
"pk": 1,
4141
"int_field": 1,
42-
"__expandable__": ["expandable_dict", "expandable_string"],
42+
"expandable_generic": {"id": "1234"},
43+
"__expandable__": [
44+
"expandable_dict",
45+
"expandable_string",
46+
"expandable_generic",
47+
],
4348
},
4449
"deep_test": {
4550
"test": {
4651
"pk": 1,
4752
"int_field": 1,
48-
"__expandable__": ["expandable_dict", "expandable_string"],
53+
"expandable_generic": {"id": "1234"},
54+
"__expandable__": [
55+
"expandable_dict",
56+
"expandable_string",
57+
"expandable_generic",
58+
],
4959
}
5060
},
5161
},

0 commit comments

Comments
 (0)