Skip to content

Commit

Permalink
Fix #4192 bug with BaseModel.construct and aliased Fields (#4191)
Browse files Browse the repository at this point in the history
* Fix a bug where BaseModel.construct would not appropriately respect field aliases

* Perhaps do not raise on construct and just apply the fix

* Fix quotes and remove check on allow_population_by_field_name

* Fix lint

* Fix lint, remove bad arg

* Black formatted

* Mmmm black formatter and single quotes linting, what a world

* Added change file

* PR feedback

Co-authored-by: Kyle Amos <kamos@seatgeek.com>
  • Loading branch information
kylebamos and Kyle Amos committed Aug 11, 2022
1 parent 4cb58cf commit a587ece
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 2 deletions.
1 change: 1 addition & 0 deletions changes/4192-kylebamos.md
@@ -0,0 +1 @@
Update BaseModel.construct to work with aliased Fields
4 changes: 3 additions & 1 deletion pydantic/main.py
Expand Up @@ -588,7 +588,9 @@ def construct(cls: Type['Model'], _fields_set: Optional['SetStr'] = None, **valu
m = cls.__new__(cls)
fields_values: Dict[str, Any] = {}
for name, field in cls.__fields__.items():
if name in values:
if field.alt_alias and field.alias in values:
fields_values[name] = values[field.alias]
elif name in values:
fields_values[name] = values[name]
elif not field.required:
fields_values[name] = field.get_default()
Expand Down
39 changes: 38 additions & 1 deletion tests/test_aliases.py
@@ -1,5 +1,6 @@
import re
from typing import Any, List, Optional
from contextlib import nullcontext as does_not_raise
from typing import Any, ContextManager, List, Optional

import pytest

Expand Down Expand Up @@ -345,3 +346,39 @@ class Model(BaseModel):
m = Model(**data)
assert m.empty_string_key == 123
assert m.dict(by_alias=True) == data


@pytest.mark.parametrize(
'use_construct, allow_population_by_field_name_config, arg_name, expectation',
[
[False, True, 'bar', does_not_raise()],
[False, True, 'bar_', does_not_raise()],
[False, False, 'bar', does_not_raise()],
[False, False, 'bar_', pytest.raises(ValueError)],
[True, True, 'bar', does_not_raise()],
[True, True, 'bar_', does_not_raise()],
[True, False, 'bar', does_not_raise()],
[True, False, 'bar_', does_not_raise()],
],
)
def test_allow_population_by_field_name_config(
use_construct: bool,
allow_population_by_field_name_config: bool,
arg_name: str,
expectation: ContextManager,
):
expected_value: int = 7

class Foo(BaseModel):
bar_: int = Field(..., alias='bar')

class Config(BaseConfig):
allow_population_by_field_name = allow_population_by_field_name_config

with expectation:
if use_construct:
f = Foo.construct(**{arg_name: expected_value})
else:
f = Foo(**{arg_name: expected_value})

assert f.bar_ == expected_value

0 comments on commit a587ece

Please sign in to comment.