From bbb91822c86a654445f83256614da60f70f87f27 Mon Sep 17 00:00:00 2001 From: PrettyWood Date: Mon, 4 Jan 2021 18:57:03 +0100 Subject: [PATCH] fix: support custom root type with nested models in `parse_obj` --- pydantic/main.py | 6 +++--- tests/test_main.py | 54 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 3 deletions(-) diff --git a/pydantic/main.py b/pydantic/main.py index 786ee22969f..6312d5eb395 100644 --- a/pydantic/main.py +++ b/pydantic/main.py @@ -662,14 +662,14 @@ def __get_validators__(cls) -> 'CallableGenerator': @classmethod def validate(cls: Type['Model'], value: Any) -> 'Model': - if isinstance(value, dict): + if cls.__custom_root_type__: + return cls.parse_obj(value) + elif isinstance(value, dict): return cls(**value) elif isinstance(value, cls): return value.copy() elif cls.__config__.orm_mode: return cls.from_orm(value) - elif cls.__custom_root_type__: - return cls.parse_obj(value) else: try: value_as_dict = dict(value) diff --git a/tests/test_main.py b/tests/test_main.py index e026f59d058..5f9d3e75d51 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -1120,6 +1120,60 @@ class MyModel(BaseModel): ] +def test_parse_obj_nested_root(): + class Pokemon(BaseModel): + name: str + level: int + + class Pokemons(BaseModel): + __root__: List[Pokemon] + + class Player(BaseModel): + rank: int + pokemons: Pokemons + + class Players(BaseModel): + __root__: Dict[str, Player] + + class Tournament(BaseModel): + players: Players + city: str + + payload = { + 'players': { + 'Jane': { + 'rank': 1, + 'pokemons': [ + { + 'name': 'Pikachu', + 'level': 100, + }, + { + 'name': 'Bulbasaur', + 'level': 13, + }, + ], + }, + 'Tarzan': { + 'rank': 2, + 'pokemons': [ + { + 'name': 'Jigglypuff', + 'level': 7, + }, + ], + }, + }, + 'city': 'Qwerty', + } + + tournament = Tournament.parse_obj(payload) + assert tournament.city == 'Qwerty' + assert len(tournament.players.__root__) == 2 + assert len(tournament.players.__root__['Jane'].pokemons.__root__) == 2 + assert tournament.players.__root__['Jane'].pokemons.__root__[0].name == 'Pikachu' + + def test_untouched_types(): from pydantic import BaseModel