From d5d1b8a1659aee563c9ee46946d38421bc2965c6 Mon Sep 17 00:00:00 2001 From: Samuel Colvin Date: Wed, 16 Oct 2019 14:31:37 +0100 Subject: [PATCH 1/3] Change alias precedence to prefer child models --- changes/904-samuelcolvin.md | 2 ++ pydantic/fields.py | 6 +++--- tests/test_edge_cases.py | 40 +++++++++++++++++++++++++++++++++++++ 3 files changed, 45 insertions(+), 3 deletions(-) create mode 100644 changes/904-samuelcolvin.md diff --git a/changes/904-samuelcolvin.md b/changes/904-samuelcolvin.md new file mode 100644 index 00000000000..690634ba4f6 --- /dev/null +++ b/changes/904-samuelcolvin.md @@ -0,0 +1,2 @@ +**Breaking Change:** Change the precedence of aliases so child model aliases override child aliases, +including use `alias_generator` diff --git a/pydantic/fields.py b/pydantic/fields.py index 0983aa69497..388655a8c8f 100644 --- a/pydantic/fields.py +++ b/pydantic/fields.py @@ -263,10 +263,10 @@ def infer( def set_config(self, config: Type['BaseConfig']) -> None: self.model_config = config - schema_from_config = config.get_field_info(self.name) - if schema_from_config: + info_from_config = config.get_field_info(self.name) + if info_from_config: self.field_info = cast(FieldInfo, self.field_info) - self.field_info.alias = self.field_info.alias or schema_from_config.get('alias') or self.name + self.field_info.alias = info_from_config.get('alias') or self.field_info.alias or self.name self.alias = cast(str, self.field_info.alias) @property diff --git a/tests/test_edge_cases.py b/tests/test_edge_cases.py index 950b7fbfbb6..16c98442ed7 100644 --- a/tests/test_edge_cases.py +++ b/tests/test_edge_cases.py @@ -1079,3 +1079,43 @@ class Model(BaseModel): assert Model().__fields__.keys() == {'v'} assert Model.__fields__.keys() == {'v'} + + +def test_alias_child_precedence(): + class Parent(BaseModel): + x: int + + class Config: + fields = {'x': 'x1'} + + class Child(Parent): + y: int + + class Config: + fields = {'y': 'y2', 'x': 'x2'} + + assert Child.__fields__['y'].alias == 'y2' + assert Child.__fields__['x'].alias == 'x2' + + +def test_alias_generator_parent(): + class Parent(BaseModel): + x: int + + class Config: + allow_population_by_field_name = True + + @classmethod + def alias_generator(cls, f_name): + return f_name + '1' + + class Child(Parent): + y: int + + class Config: + @classmethod + def alias_generator(cls, f_name): + return f_name + '2' + + assert Child.__fields__['y'].alias == 'y2' + assert Child.__fields__['x'].alias == 'x2' From 4e85e0c0b7027d5f25db272faa6d41d760373949 Mon Sep 17 00:00:00 2001 From: Samuel Colvin Date: Wed, 16 Oct 2019 14:42:54 +0100 Subject: [PATCH 2/3] add docs about alias precedence --- docs/examples/alias_generator_config.py | 3 +-- docs/examples/alias_precedence.py | 21 +++++++++++++++++++++ docs/usage/model_config.md | 13 +++++++++++++ mkdocs.yml | 2 +- 4 files changed, 36 insertions(+), 3 deletions(-) create mode 100644 docs/examples/alias_precedence.py diff --git a/docs/examples/alias_generator_config.py b/docs/examples/alias_generator_config.py index c4f207c643f..d957bba604b 100644 --- a/docs/examples/alias_generator_config.py +++ b/docs/examples/alias_generator_config.py @@ -5,12 +5,11 @@ def to_camel(string: str) -> str: class Voice(BaseModel): name: str - gender: str language_code: str class Config: alias_generator = to_camel -voice = Voice(Name='Filiz', Gender='Female', LanguageCode='tr-TR') +voice = Voice(Name='Filiz', LanguageCode='tr-TR') print(voice.language_code) print(voice.dict(by_alias=True)) diff --git a/docs/examples/alias_precedence.py b/docs/examples/alias_precedence.py new file mode 100644 index 00000000000..a6407f9b705 --- /dev/null +++ b/docs/examples/alias_precedence.py @@ -0,0 +1,21 @@ +from pydantic import BaseModel + +class Voice(BaseModel): + name: str + language_code: str + + class Config: + @classmethod + def alias_generator(cls, string: str) -> str: + # this is the same as `alias_generator = to_camel` above + return ''.join(word.capitalize() for word in string.split('_')) + +class Character(Voice): + mood: str + + class Config: + fields = {'mood': 'Mood', 'language_code': 'lang'} + +c = Character(Mood='happy', Name='Filiz', lang='tr-TR') +print(c) +print(c.dict(by_alias=True)) diff --git a/docs/usage/model_config.md b/docs/usage/model_config.md index 1c435036194..493350469d3 100644 --- a/docs/usage/model_config.md +++ b/docs/usage/model_config.md @@ -98,3 +98,16 @@ you can automatically generate aliases using `alias_generator`: {!.tmp_examples/alias_generator_config.py!} ``` _(This script is complete, it should run "as is")_ + + +## Alias Precedence + +Aliases defined on the `Config` class of child models will take priority over any aliases defined on `Config` of a +parent model: + +```py +{!.tmp_examples/alias_precedence.py!} +``` +_(This script is complete, it should run "as is")_ + +This includes when a child model uses `alias_generator` where the aliases of all parent model fields will be updated. diff --git a/mkdocs.yml b/mkdocs.yml index 1f32f7cff64..398ab9a7331 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -29,7 +29,7 @@ nav: - usage/models.md - 'Field Types': usage/types.md - usage/validators.md - - usage/model_config.md + - 'Model Config': usage/model_config.md - usage/schema.md - usage/exporting_models.md - usage/dataclasses.md From 39fdd7f53a508e6f311f8aa6fe3b16892c7a5902 Mon Sep 17 00:00:00 2001 From: Samuel Colvin Date: Thu, 17 Oct 2019 10:27:51 +0100 Subject: [PATCH 3/3] correct change --- changes/904-samuelcolvin.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/changes/904-samuelcolvin.md b/changes/904-samuelcolvin.md index 690634ba4f6..e54ecc6f842 100644 --- a/changes/904-samuelcolvin.md +++ b/changes/904-samuelcolvin.md @@ -1,2 +1,2 @@ -**Breaking Change:** Change the precedence of aliases so child model aliases override child aliases, -including use `alias_generator` +**Breaking Change:** Change the precedence of aliases so child model aliases override parent aliases, +including using `alias_generator`