Browse Source

fix: support nested include/exclude in jsonable_encoder for dicts and dataclasses

pull/15972/head
Himanshu 2 weeks ago
parent
commit
8f5a755ec0
  1. 31
      fastapi/encoders.py
  2. 33
      tests/test_jsonable_encoder.py

31
fastapi/encoders.py

@ -280,11 +280,20 @@ def jsonable_encoder(
return None return None
if isinstance(obj, dict): if isinstance(obj, dict):
encoded_dict = {} encoded_dict = {}
allowed_keys = set(obj.keys())
if include is not None: if include is not None:
allowed_keys &= set(include) if isinstance(include, dict):
allowed_keys = set(obj.keys()) & set(include.keys())
else:
allowed_keys = set(obj.keys()) & set(include)
else:
allowed_keys = set(obj.keys())
if exclude is not None: if exclude is not None:
allowed_keys -= set(exclude) if isinstance(exclude, dict):
for k, v in exclude.items():
if v is True:
allowed_keys.discard(k)
else:
allowed_keys -= set(exclude)
for key, value in obj.items(): for key, value in obj.items():
if ( if (
( (
@ -295,6 +304,20 @@ def jsonable_encoder(
and (value is not None or not exclude_none) and (value is not None or not exclude_none)
and key in allowed_keys and key in allowed_keys
): ):
next_include = None
if include is not None:
if isinstance(include, dict):
next_include = include.get(key)
if not isinstance(next_include, (dict, set)):
next_include = None
next_exclude = None
if exclude is not None:
if isinstance(exclude, dict):
next_exclude = exclude.get(key)
if not isinstance(next_exclude, (dict, set)):
next_exclude = None
encoded_key = jsonable_encoder( encoded_key = jsonable_encoder(
key, key,
by_alias=by_alias, by_alias=by_alias,
@ -305,6 +328,8 @@ def jsonable_encoder(
) )
encoded_value = jsonable_encoder( encoded_value = jsonable_encoder(
value, value,
include=next_include,
exclude=next_exclude,
by_alias=by_alias, by_alias=by_alias,
exclude_unset=exclude_unset, exclude_unset=exclude_unset,
exclude_none=exclude_none, exclude_none=exclude_none,

33
tests/test_jsonable_encoder.py

@ -329,3 +329,36 @@ def test_encode_color(module_path):
data = {"color": Color("blue")} data = {"color": Color("blue")}
assert jsonable_encoder(data) == {"color": "blue"} assert jsonable_encoder(data) == {"color": "blue"}
def test_encode_dict_nested_exclude():
pet = {"name": "Firulais", "owner": {"name": "Foo", "age": 20}}
assert jsonable_encoder(pet, exclude={"owner": {"age"}}) == {
"name": "Firulais",
"owner": {"name": "Foo"},
}
def test_encode_dict_nested_include():
pet = {"name": "Firulais", "owner": {"name": "Foo", "age": 20}}
assert jsonable_encoder(pet, include={"owner": {"name"}}) == {
"owner": {"name": "Foo"}
}
def test_encode_dataclass_nested_exclude():
@dataclass
class Owner:
name: str
age: int
@dataclass
class PetWithNested:
name: str
owner: Owner
pet = PetWithNested(name="Firulais", owner=Owner(name="Foo", age=20))
assert jsonable_encoder(pet, exclude={"owner": {"age"}}) == {
"name": "Firulais",
"owner": {"name": "Foo"},
}

Loading…
Cancel
Save