From 62d39d90a7aa5acb2a78312fb2de009da9e98f1f Mon Sep 17 00:00:00 2001 From: Ben Demaree Date: Thu, 24 May 2018 10:46:03 -0500 Subject: [PATCH] Make model dump/load symmetric for aliased fields (#160) * Make dump/load symmetric for aliased fields * Only populate model fields by alias if enabled * Document Config.allow_population_by_alias * Update history * Fix doc formatting * Fix doc errors --- HISTORY.rst | 4 ++++ docs/index.rst | 13 +++++++++++++ pydantic/main.py | 4 ++++ tests/test_main.py | 14 ++++++++++++++ 4 files changed, 35 insertions(+) diff --git a/HISTORY.rst b/HISTORY.rst index 088b051..e5672ed 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -3,6 +3,10 @@ History ------- +v0.9.2 (2018-XX-XX) +................... +* add ``Config.allow_population_by_alias`` #160, thanks @bendemaree + v0.9.1 (2018-05-10) ................... * allow custom ``get_field_config`` on config classes #159 diff --git a/docs/index.rst b/docs/index.rst index ba26e75..e8cda3e 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -262,6 +262,19 @@ Options: rather than the raw enum - useful if you want to serialise ``model.dict()`` later (default: ``False``) :fields: extra information on each field, currently just "alias" is allowed (default: ``None``) :validate_assignment: whether to perform validation on assignment to attributes or not (default: ``False``) +:allow_population_by_alias: whether or not an aliased field may be populated by its name as given by the model + attribute, rather than strictly the alias; please be sure to read the warning below before enabling this (default: + ``False``) + +.. warning:: + + Think twice before enabling ``allow_population_by_alias``! Enabling it could cause previously correct code to become + subtly incorrect. As an example, say you have a field named ``card_number`` with the alias ``cardNumber``. With + population by alias disabled (the default), trying to parse an object with only the key ``card_number`` will fail. + However, if you enable population by alias, the ``card_number`` field can now be populated from ``cardNumber`` + **or** ``card_number``, and the previously-invalid example object would now be valid. This may be desired for some + use cases, but in others (like the one given here, perhaps!), relaxing strictness with respect to aliases could + introduce bugs. .. literalinclude:: examples/config.py diff --git a/pydantic/main.py b/pydantic/main.py index c3834cf..f638fa5 100644 --- a/pydantic/main.py +++ b/pydantic/main.py @@ -24,6 +24,7 @@ class BaseConfig: ignore_extra = True allow_extra = False allow_mutation = True + allow_population_by_alias = False use_enum_values = False fields = {} validate_assignment = False @@ -265,6 +266,9 @@ class BaseModel(metaclass=MetaModel): for name, field in self.__fields__.items(): value = input_data.get(field.alias, MISSING) + if value is MISSING and self.__config__.allow_population_by_alias and field.alt_alias: + value = input_data.get(field.name, MISSING) + if value is MISSING: if self.__config__.validate_all or field.validate_always: value = field.default diff --git a/tests/test_main.py b/tests/test_main.py index 17c7999..af2b520 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -292,6 +292,20 @@ def test_alias(): assert Model(_a='different').dict() == {'a': 'different'} +def test_population_by_alias(): + class Model(BaseModel): + a: str + + class Config: + allow_population_by_alias = True + fields = { + 'a': {'alias': '_a'} + } + + assert Model(a='different').a == 'different' + assert Model(a='different').dict() == {'a': 'different'} + + def test_field_order(): class Model(BaseModel): c: float