From 8f2b00490a168685f3b9f33609180986b32dd5b6 Mon Sep 17 00:00:00 2001 From: PrettyWood Date: Mon, 6 Sep 2021 22:49:36 +0200 Subject: [PATCH] fix: nested ORM from nested dictionaries closes #3181 --- pydantic/main.py | 8 +++++--- tests/test_orm_mode.py | 27 ++++++++++++++++++++++++--- 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/pydantic/main.py b/pydantic/main.py index a25e96efea..1c81e10db9 100644 --- a/pydantic/main.py +++ b/pydantic/main.py @@ -656,10 +656,10 @@ def validate(cls: Type['Model'], value: Any) -> 'Model': value = cls._enforce_dict_if_root(value) - if cls.__config__.orm_mode: - return cls.from_orm(value) - elif isinstance(value, dict): + if isinstance(value, dict): return cls(**value) + elif cls.__config__.orm_mode: + return cls.from_orm(value) else: try: value_as_dict = dict(value) @@ -669,6 +669,8 @@ def validate(cls: Type['Model'], value: Any) -> 'Model': @classmethod def _decompose_class(cls: Type['Model'], obj: Any) -> GetterDict: + if isinstance(obj, GetterDict): + return obj return cls.__config__.getter_dict(obj) @classmethod diff --git a/tests/test_orm_mode.py b/tests/test_orm_mode.py index 67f44e9bd4..36a8c8c041 100644 --- a/tests/test_orm_mode.py +++ b/tests/test_orm_mode.py @@ -1,3 +1,4 @@ +from types import SimpleNamespace from typing import Any, Dict, List import pytest @@ -305,15 +306,14 @@ class Config: def test_recursive_parsing(): - from types import SimpleNamespace - class Getter(GetterDict): # try to read the modified property name # either as an attribute or as a key def get(self, key, default): key = key + key try: - return self._obj[key] + v = self._obj[key] + return Getter(v) if isinstance(v, dict) else v except TypeError: return getattr(self._obj, key, default) except KeyError: @@ -337,3 +337,24 @@ class ModelB(Model): # test recursive parsing with dict keys obj = dict(bb=dict(aa=1)) assert ModelB.from_orm(obj) == ModelB(b=ModelA(a=1)) + + +def test_nested_orm(): + class User(BaseModel): + first_name: str + last_name: str + + class Config: + orm_mode = True + + class State(BaseModel): + user: User + + class Config: + orm_mode = True + + # Pass an "orm instance" + State.from_orm(SimpleNamespace(user=SimpleNamespace(first_name='John', last_name='Appleseed'))) + + # Pass dictionary data directly + State(**{'user': {'first_name': 'John', 'last_name': 'Appleseed'}})