Fix #4192 bug with BaseModel.construct and aliased Fields (#4191)

* 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>
This commit is contained in:
Kyle Amos
2022-08-11 06:49:07 -04:00
committed by GitHub
parent 4cb58cf9df
commit a587ecee88
3 changed files with 42 additions and 2 deletions
+1
View File
@@ -0,0 +1 @@
Update BaseModel.construct to work with aliased Fields
+3 -1
View File
@@ -588,7 +588,9 @@ class BaseModel(Representation, metaclass=ModelMetaclass):
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()
+38 -1
View File
@@ -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
@@ -345,3 +346,39 @@ def test_empty_string_alias():
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