Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug fix: excludes skipped for aliased nested fields when by_alias=True #1398

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions changes/1397-AlexECX.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Always use a field's real name with includes/excludes in `model._iter()`, regardless of `by_alias`.
18 changes: 10 additions & 8 deletions pydantic/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -690,27 +690,29 @@ def _iter(
value_exclude = ValueItems(self, exclude) if exclude else None
value_include = ValueItems(self, include) if include else None

for k, v in self.__dict__.items():
for field_key, v in self.__dict__.items():
if (
(allowed_keys is not None and k not in allowed_keys)
(allowed_keys is not None and field_key not in allowed_keys)
or (exclude_none and v is None)
or (exclude_defaults and self.__field_defaults__.get(k, _missing) == v)
or (exclude_defaults and self.__field_defaults__.get(field_key, _missing) == v)
):
continue
if by_alias and k in self.__fields__:
k = self.__fields__[k].alias
if by_alias and field_key in self.__fields__:
dict_key = self.__fields__[field_key].alias
else:
dict_key = field_key
if to_dict or value_include or value_exclude:
v = self._get_value(
v,
to_dict=to_dict,
by_alias=by_alias,
include=value_include and value_include.for_element(k),
exclude=value_exclude and value_exclude.for_element(k),
include=value_include and value_include.for_element(field_key),
exclude=value_exclude and value_exclude.for_element(field_key),
exclude_unset=exclude_unset,
exclude_defaults=exclude_defaults,
exclude_none=exclude_none,
)
yield k, v
yield dict_key, v

def _calculate_keys(
self,
Expand Down
30 changes: 29 additions & 1 deletion tests/test_edge_cases.py
Original file line number Diff line number Diff line change
Expand Up @@ -436,7 +436,35 @@ class Model(BaseModel):
assert m.dict(exclude={'e': ..., 'f': {'d'}}) == {'f': {'c': 'foo'}}


def test_advanced_value_inclide():
def test_advanced_exclude_by_alias():
class SubSubModel(BaseModel):
a: str
aliased_b: str = Field(..., alias='b_alias')

class SubModel(BaseModel):
aliased_c: str = Field(..., alias='c_alias')
aliased_d: List[SubSubModel] = Field(..., alias='d_alias')

class Model(BaseModel):
aliased_e: str = Field(..., alias='e_alias')
aliased_f: SubModel = Field(..., alias='f_alias')

m = Model(
e_alias='e',
f_alias=SubModel(c_alias='foo', d_alias=[SubSubModel(a='a', b_alias='b'), SubSubModel(a='c', b_alias='e')]),
)

excludes = {'aliased_f': {'aliased_c': ..., 'aliased_d': {-1: {'a'}}}}
assert m.dict(exclude=excludes, by_alias=True) == {
'e_alias': 'e',
'f_alias': {'d_alias': [{'a': 'a', 'b_alias': 'b'}, {'b_alias': 'e'}]},
}

excludes = {'aliased_e': ..., 'aliased_f': {'aliased_d'}}
assert m.dict(exclude=excludes, by_alias=True) == {'f_alias': {'c_alias': 'foo'}}


def test_advanced_value_include():
class SubSubModel(BaseModel):
a: str
b: str
Expand Down