diff --git a/ninja/schema.py b/ninja/schema.py index 55a60a84c..46136f7a9 100644 --- a/ninja/schema.py +++ b/ninja/schema.py @@ -66,21 +66,30 @@ def __getattr__(self, key: str) -> Any: if resolver: value = resolver(getter=self) else: + resolve = False if isinstance(self._obj, dict): - if key not in self._obj: + if key in self._obj: + value = self._obj[key] + elif "." in key and not key.startswith("_"): + # Only do template lookup if necessary, which will also work around + # Variable.resolve() returning default value of defaultdict's + resolve = True + else: raise AttributeError(key) - value = self._obj[key] else: try: value = getattr(self._obj, key) except AttributeError: - try: - # value = attrgetter(key)(self._obj) - value = Variable(key).resolve(self._obj) - # TODO: Variable(key) __init__ is actually slower than - # Variable.resolve - so it better be cached - except VariableDoesNotExist as e: - raise AttributeError(key) from e + resolve = True + + if resolve and not key.startswith("_"): + try: + # value = attrgetter(key)(self._obj) + value = Variable(key).resolve(self._obj) + # TODO: Variable(key) __init__ is actually slower than + # Variable.resolve - so it better be cached + except VariableDoesNotExist as e: + raise AttributeError(key) from e return self._convert_result(value) # def get(self, key: Any, default: Any = None) -> Any: diff --git a/tests/test_schema.py b/tests/test_schema.py index 8f92d5201..c7d630307 100644 --- a/tests/test_schema.py +++ b/tests/test_schema.py @@ -68,6 +68,11 @@ class UserSchema(Schema): avatar: Optional[str] = None +class BossSchema(Schema): + name: str + title: str + + class UserWithBossSchema(UserSchema): boss: Optional[str] = Field(None, alias="boss.name") has_boss: bool @@ -78,6 +83,11 @@ def resolve_has_boss(obj): return bool(obj.boss) +class NestedUserWithBossSchema(UserSchema): + boss: Optional[BossSchema] = None + boss_name: Optional[str] = Field(None, alias="boss.name") + + class UserWithInitialsSchema(UserWithBossSchema): initials: str @@ -144,6 +154,21 @@ def test_with_boss_schema(): } +def test_boss_schema_as_nested_dict(): + user = User() + schema = NestedUserWithBossSchema.from_orm(user) + + result1 = schema.dict(by_alias=True) + result1_no_boss_name = result1.copy() + result1_no_boss_name.pop("boss.name") + + result2 = NestedUserWithBossSchema.from_orm(result1_no_boss_name).dict( + by_alias=True + ) + + assert result1 == result2 + + SKIP_NON_STATIC_RESOLVES = True