From 711ac4f21288853d2d2e0da3bce753c39d00f191 Mon Sep 17 00:00:00 2001 From: SergeKu <46962368+SergeKu@users.noreply.github.com> Date: Wed, 10 Sep 2025 16:45:50 +0300 Subject: [PATCH] Added processing of structures with None Sometimes we need to convert structures like Union[Model1, Model2, None] to Union[all_fields_optional(Model1), all_fields_optional(Model2), None]. For this purpose parameter "struct_processing" added some functions. --- pydantic_partial/partial.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/pydantic_partial/partial.py b/pydantic_partial/partial.py index 82e383b..6dc9720 100644 --- a/pydantic_partial/partial.py +++ b/pydantic_partial/partial.py @@ -47,6 +47,7 @@ def create_partial_model( *fields: str, recursive: bool = False, partial_cls_name: Optional[str] = None, + struct_processing: bool = False ) -> type[SelfT]: # Convert one type to being partial - if possible def _partial_annotation_arg(field_name_: str, field_annotation: type) -> type: @@ -63,7 +64,7 @@ def _partial_annotation_arg(field_name_: str, field_annotation: type) -> type: ] if children_fields == ["*"]: children_fields = [] - return field_annotation.model_as_partial(*children_fields, recursive=recursive) + return field_annotation.model_as_partial(*children_fields, recursive=recursive, struct_processing=struct_processing) else: return field_annotation @@ -99,13 +100,13 @@ def _partial_annotation_arg(field_name_: str, field_annotation: type) -> type: field_annotation_origin = Union field_annotation = field_annotation_origin[ # type: ignore tuple( # type: ignore - _partial_annotation_arg(field_name, field_annotation_arg) + _partial_annotation_arg(field_name, field_annotation_arg ) for field_annotation_arg in get_args(field_annotation) ) ] else: - field_annotation = _partial_annotation_arg(field_name, field_annotation) + field_annotation = _partial_annotation_arg(field_name, field_annotation ) # Construct new field definition if field_name in fields_: @@ -116,6 +117,10 @@ def _partial_annotation_arg(field_name_: str, field_annotation: type) -> type: and isinstance(field_info.json_schema_extra, dict) and field_info.json_schema_extra.get("required", False) ) + or ( # for structures (Union etc.) with None processing + struct_processing + and field_annotation_origin in (Union, UnionType, tuple, list, set, dict) + ) ): optional_fields[field_name] = ( Optional[field_annotation], @@ -158,10 +163,11 @@ def model_as_partial( *fields: str, recursive: bool = False, partial_cls_name: Optional[str] = None, + struct_processing: bool = False ) -> type[ModelSelfT]: return cast( type[ModelSelfT], - create_partial_model(cls, *fields, recursive=recursive, partial_cls_name=partial_cls_name), + create_partial_model(cls, *fields, recursive=recursive, partial_cls_name=partial_cls_name, struct_processing=struct_processing), ) @classmethod @@ -170,10 +176,11 @@ def as_partial( *fields: str, recursive: bool = False, partial_cls_name: Optional[str] = None, + struct_processing: bool = False ) -> type[ModelSelfT]: warnings.warn( "as_partial(...) is deprecated, use model_as_partial(...) instead", DeprecationWarning, stacklevel=2, ) - return cls.model_as_partial(*fields, recursive=recursive, partial_cls_name=partial_cls_name) + return cls.model_as_partial(*fields, recursive=recursive, partial_cls_name=partial_cls_name, struct_processing=struct_processing)