diff --git a/changes/1736-PrettyWood.md b/changes/1736-PrettyWood.md new file mode 100644 index 0000000..64a22ab --- /dev/null +++ b/changes/1736-PrettyWood.md @@ -0,0 +1 @@ +Fix behaviour with forward refs and optional fields in nested models \ No newline at end of file diff --git a/pydantic/fields.py b/pydantic/fields.py index 6ab039c..c162616 100644 --- a/pydantic/fields.py +++ b/pydantic/fields.py @@ -334,6 +334,11 @@ class ModelField(Representation): """ self._set_default_and_type() + if self.type_.__class__ == ForwardRef: + # self.type_ is currently a ForwardRef and there's nothing we can do now, + # user will need to call model.update_forward_refs() + return + self._type_analysis() if self.required is Undefined: self.required = True @@ -366,11 +371,6 @@ class ModelField(Representation): if self.type_ is None: raise errors_.ConfigError(f'unable to infer type for attribute "{self.name}"') - if self.type_.__class__ == ForwardRef: - # self.type_ is currently a ForwardRef and there's nothing we can do now, - # user will need to call model.update_forward_refs() - return - if self.required is False and default_value is None: self.allow_none = True diff --git a/tests/test_forward_ref.py b/tests/test_forward_ref.py index d640dd7..420079d 100644 --- a/tests/test_forward_ref.py +++ b/tests/test_forward_ref.py @@ -439,3 +439,36 @@ else: raise AssertionError('error not raised') """ ) + + +@skip_pre_37 +def test_forward_ref_optional(create_module): + module = create_module( + """ +from __future__ import annotations +from pydantic import BaseModel, Field +from typing import List, Optional + + +class Spec(BaseModel): + spec_fields: List[str] = Field(..., alias="fields") + filter: Optional[str] + sort: Optional[str] + + +class PSpec(Spec): + g: Optional[GSpec] + + +class GSpec(Spec): + p: Optional[PSpec] + +PSpec.update_forward_refs() + +class Filter(BaseModel): + g: Optional[GSpec] + p: Optional[PSpec] + """ + ) + Filter = module.Filter + assert isinstance(Filter(p={'sort': 'some_field:asc', 'fields': []}), Filter)