strawchemy 0.2.8__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- strawchemy/__init__.py +13 -0
- strawchemy/config.py +29 -0
- strawchemy/dto/__init__.py +19 -0
- strawchemy/dto/backend/__init__.py +0 -0
- strawchemy/dto/backend/dataclass.py +164 -0
- strawchemy/dto/backend/pydantic.py +147 -0
- strawchemy/dto/base.py +563 -0
- strawchemy/dto/constants.py +5 -0
- strawchemy/dto/exceptions.py +12 -0
- strawchemy/dto/factories/__init__.py +0 -0
- strawchemy/dto/factories/pydantic_sqlalchemy.py +15 -0
- strawchemy/dto/inspectors/__init__.py +0 -0
- strawchemy/dto/inspectors/sqlalchemy.py +274 -0
- strawchemy/dto/types.py +125 -0
- strawchemy/dto/utils.py +71 -0
- strawchemy/graph.py +438 -0
- strawchemy/graphql/__init__.py +16 -0
- strawchemy/graphql/constants.py +24 -0
- strawchemy/graphql/dto.py +596 -0
- strawchemy/graphql/factory.py +1281 -0
- strawchemy/graphql/filters.py +327 -0
- strawchemy/graphql/inspector.py +21 -0
- strawchemy/graphql/typing.py +40 -0
- strawchemy/mapper.py +187 -0
- strawchemy/pydantic.py +19 -0
- strawchemy/sqlalchemy/__init__.py +13 -0
- strawchemy/sqlalchemy/_executor.py +233 -0
- strawchemy/sqlalchemy/_query.py +343 -0
- strawchemy/sqlalchemy/_scope.py +478 -0
- strawchemy/sqlalchemy/_transpiler.py +744 -0
- strawchemy/sqlalchemy/exceptions.py +12 -0
- strawchemy/sqlalchemy/filters/__init__.py +29 -0
- strawchemy/sqlalchemy/filters/base.py +380 -0
- strawchemy/sqlalchemy/filters/geo.py +53 -0
- strawchemy/sqlalchemy/hook.py +62 -0
- strawchemy/sqlalchemy/inspector.py +86 -0
- strawchemy/sqlalchemy/repository/__init__.py +7 -0
- strawchemy/sqlalchemy/repository/_async.py +102 -0
- strawchemy/sqlalchemy/repository/_base.py +68 -0
- strawchemy/sqlalchemy/repository/_sync.py +104 -0
- strawchemy/sqlalchemy/typing.py +68 -0
- strawchemy/strawberry/__init__.py +7 -0
- strawchemy/strawberry/_field.py +369 -0
- strawchemy/strawberry/_instance.py +27 -0
- strawchemy/strawberry/_registry.py +221 -0
- strawchemy/strawberry/_utils.py +75 -0
- strawchemy/strawberry/exceptions.py +6 -0
- strawchemy/strawberry/factory.py +882 -0
- strawchemy/strawberry/inspector.py +73 -0
- strawchemy/strawberry/pydantic.py +396 -0
- strawchemy/strawberry/repository/__init__.py +6 -0
- strawchemy/strawberry/repository/_async.py +264 -0
- strawchemy/strawberry/repository/_node.py +114 -0
- strawchemy/strawberry/repository/_sync.py +273 -0
- strawchemy/strawberry/typing.py +28 -0
- strawchemy/testing/__init__.py +0 -0
- strawchemy/testing/pytest.py +87 -0
- strawchemy/typing.py +7 -0
- strawchemy/utils.py +49 -0
- strawchemy-0.2.8.dist-info/METADATA +19 -0
- strawchemy-0.2.8.dist-info/RECORD +63 -0
- strawchemy-0.2.8.dist-info/WHEEL +4 -0
- strawchemy-0.2.8.dist-info/licenses/LICENCE +21 -0
strawchemy/__init__.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""Custom DTO implementation."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from .mapper import Strawchemy
|
|
6
|
+
from .sqlalchemy.hook import QueryHook
|
|
7
|
+
from .strawberry import ModelInstance
|
|
8
|
+
from .strawberry.repository import StrawchemyAsyncRepository
|
|
9
|
+
|
|
10
|
+
__all__ = ("ModelInstance", "QueryHook", "StrawchemyAsyncRepository", "strawchemy")
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
strawchemy = Strawchemy()
|
strawchemy/config.py
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
5
|
+
|
|
6
|
+
from .sqlalchemy import SQLAlchemyGraphQLInspector
|
|
7
|
+
from .strawberry import default_session_getter
|
|
8
|
+
from .strawberry.repository import StrawchemyAsyncRepository, StrawchemySyncRepository
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
from .graphql.inspector import GraphQLInspectorProtocol
|
|
14
|
+
from .sqlalchemy.typing import FilterMap
|
|
15
|
+
from .strawberry.typing import AsyncSessionGetter
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass
|
|
19
|
+
class StrawchemyConfig:
|
|
20
|
+
session_getter: AsyncSessionGetter = default_session_getter
|
|
21
|
+
auto_snake_case: bool = True
|
|
22
|
+
repository_type: type[StrawchemyAsyncRepository[Any] | StrawchemySyncRepository[Any]] = StrawchemyAsyncRepository
|
|
23
|
+
filter_overrides: FilterMap | None = None
|
|
24
|
+
execution_options: dict[str, Any] | None = None
|
|
25
|
+
|
|
26
|
+
inspector: GraphQLInspectorProtocol[Any, Any] = field(init=False)
|
|
27
|
+
|
|
28
|
+
def __post_init__(self) -> None:
|
|
29
|
+
self.inspector = SQLAlchemyGraphQLInspector(filter_overrides=self.filter_overrides)
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""Custom DTO implementation."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from .base import DTOFieldDefinition, ModelFieldT, ModelInspector, ModelT
|
|
6
|
+
from .types import DTOConfig, Purpose, PurposeConfig
|
|
7
|
+
from .utils import config, field
|
|
8
|
+
|
|
9
|
+
__all__ = (
|
|
10
|
+
"DTOConfig",
|
|
11
|
+
"DTOFieldDefinition",
|
|
12
|
+
"ModelFieldT",
|
|
13
|
+
"ModelInspector",
|
|
14
|
+
"ModelT",
|
|
15
|
+
"Purpose",
|
|
16
|
+
"PurposeConfig",
|
|
17
|
+
"config",
|
|
18
|
+
"field",
|
|
19
|
+
)
|
|
File without changes
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
"""Data Transfer Object (DTO) classes for dataclasses.
|
|
2
|
+
|
|
3
|
+
This module defines base classes and utilities for working with DTOs that
|
|
4
|
+
are based on Python dataclasses. It provides a way to map data between
|
|
5
|
+
dataclasses and other data models, such as SQLAlchemy models.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import dataclasses
|
|
11
|
+
from dataclasses import dataclass
|
|
12
|
+
from inspect import getmodule
|
|
13
|
+
from typing import TYPE_CHECKING, Any, NamedTuple, TypeAlias, TypeVar, cast, override
|
|
14
|
+
|
|
15
|
+
from strawchemy.dto.base import DTOBackend, DTOBase, MappedDTO, ModelFieldT, ModelT
|
|
16
|
+
from strawchemy.dto.types import DTO_MISSING, DTOMissingType
|
|
17
|
+
|
|
18
|
+
if TYPE_CHECKING:
|
|
19
|
+
from collections.abc import Callable, Iterable
|
|
20
|
+
|
|
21
|
+
from strawchemy.dto.base import DTOFieldDefinition
|
|
22
|
+
|
|
23
|
+
__all__ = ("DataclassDTO", "DataclassDTOBackend", "DataclassDTOT", "MappedDataclassDTO")
|
|
24
|
+
|
|
25
|
+
DataclassDTOT = TypeVar("DataclassDTOT", bound="DataclassDTO[Any] | MappedDataclassDTO[Any]")
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class DataclassDTO(DTOBase[ModelT]): ...
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class BasicFieldInfo(NamedTuple):
|
|
32
|
+
name: str
|
|
33
|
+
type: Any
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class FullFieldInfo(NamedTuple):
|
|
37
|
+
name: str
|
|
38
|
+
type: Any
|
|
39
|
+
field: dataclasses.Field[Any]
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
DataclassFieldInfo: TypeAlias = BasicFieldInfo | FullFieldInfo
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@dataclass
|
|
46
|
+
class MappedDataclassDTO(MappedDTO[ModelT]):
|
|
47
|
+
@override
|
|
48
|
+
def to_mapped(self, **override: Any) -> ModelT:
|
|
49
|
+
"""Create an instance of `self.__sqla_model__`.
|
|
50
|
+
|
|
51
|
+
Fill the bound SQLAlchemy model recursively with values from this dataclass.
|
|
52
|
+
"""
|
|
53
|
+
as_model = {}
|
|
54
|
+
dc_fields: dict[str, dataclasses.Field[Any]] = {}
|
|
55
|
+
if dataclasses.is_dataclass(self.__dto_model__):
|
|
56
|
+
dc_fields = {f.name: f for f in dataclasses.fields(self.__dto_model__)}
|
|
57
|
+
|
|
58
|
+
for dc_field in dataclasses.fields(self):
|
|
59
|
+
if (value := override.get(dc_field.name, DTO_MISSING)) and value is not DTO_MISSING:
|
|
60
|
+
as_model[dc_field.name] = value
|
|
61
|
+
continue
|
|
62
|
+
if (field := dc_fields.get(dc_field.name, None)) and not field.init:
|
|
63
|
+
continue
|
|
64
|
+
|
|
65
|
+
value: ModelT | MappedDataclassDTO[ModelT] | list[ModelT] | list[MappedDataclassDTO[ModelT]]
|
|
66
|
+
value = getattr(self, dc_field.name)
|
|
67
|
+
|
|
68
|
+
if isinstance(value, list | tuple):
|
|
69
|
+
value = [el.to_mapped() if isinstance(el, MappedDataclassDTO) else cast(ModelT, el) for el in value]
|
|
70
|
+
if isinstance(value, MappedDataclassDTO):
|
|
71
|
+
value = value.to_mapped()
|
|
72
|
+
as_model[dc_field.name] = value
|
|
73
|
+
try:
|
|
74
|
+
return self.__dto_model__(**(as_model | override))
|
|
75
|
+
except TypeError as error:
|
|
76
|
+
original_message = error.args[0] if isinstance(error.args[0], str) else repr(error)
|
|
77
|
+
msg = f"{original_message} (model: {self.__dto_model__.__name__})"
|
|
78
|
+
raise TypeError(msg) from error
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class DataclassDTOBackend(DTOBackend[DataclassDTOT]):
|
|
82
|
+
def __init__(self, dto_base: type[DataclassDTOT]) -> None:
|
|
83
|
+
self.dto_base = dto_base
|
|
84
|
+
|
|
85
|
+
def _construct_field_info(self, field_def: DTOFieldDefinition[ModelT, ModelFieldT]) -> DataclassFieldInfo:
|
|
86
|
+
if not isinstance(field_def.default_factory, DTOMissingType):
|
|
87
|
+
return FullFieldInfo(
|
|
88
|
+
field_def.name, field_def.type_, dataclasses.field(default_factory=field_def.default_factory)
|
|
89
|
+
)
|
|
90
|
+
if not isinstance(field_def.default, DTOMissingType):
|
|
91
|
+
return FullFieldInfo(field_def.name, field_def.type_, dataclasses.field(default=field_def.default))
|
|
92
|
+
|
|
93
|
+
return BasicFieldInfo(field_def.name, field_def.type_)
|
|
94
|
+
|
|
95
|
+
@override
|
|
96
|
+
def build(
|
|
97
|
+
self,
|
|
98
|
+
name: str,
|
|
99
|
+
model: type[Any],
|
|
100
|
+
field_definitions: Iterable[DTOFieldDefinition[Any, ModelFieldT]],
|
|
101
|
+
base: type[Any] | None = None,
|
|
102
|
+
repr: bool = True,
|
|
103
|
+
eq: bool = True,
|
|
104
|
+
order: bool = False,
|
|
105
|
+
unsafe_hash: bool = False,
|
|
106
|
+
frozen: bool = False,
|
|
107
|
+
match_args: bool = True,
|
|
108
|
+
kw_only: bool = True,
|
|
109
|
+
slots: bool = False,
|
|
110
|
+
**kwargs: Any,
|
|
111
|
+
) -> type[DataclassDTOT]:
|
|
112
|
+
namespace: dict[str, Any] = {}
|
|
113
|
+
fields: list[DataclassFieldInfo] = []
|
|
114
|
+
post_init_validator: list[Callable[[DataclassDTOT], None]] = []
|
|
115
|
+
|
|
116
|
+
for field in field_definitions:
|
|
117
|
+
field_info = self._construct_field_info(field)
|
|
118
|
+
fields.append(field_info)
|
|
119
|
+
|
|
120
|
+
if field.purpose_config.validator is not None:
|
|
121
|
+
|
|
122
|
+
def _validator(
|
|
123
|
+
self: DataclassDTOT,
|
|
124
|
+
_name: str = field.name,
|
|
125
|
+
_validator_function: Callable[[Any], Any] = field.purpose_config.validator,
|
|
126
|
+
) -> None:
|
|
127
|
+
value = getattr(self, _name)
|
|
128
|
+
if value is not DTO_MISSING:
|
|
129
|
+
return setattr(self, _name, _validator_function(value))
|
|
130
|
+
return setattr(self, _name, _validator_function(value))
|
|
131
|
+
|
|
132
|
+
post_init_validator.append(_validator)
|
|
133
|
+
|
|
134
|
+
if post_init_validator:
|
|
135
|
+
|
|
136
|
+
def post_init(
|
|
137
|
+
self: DataclassDTOT, _validators: list[Callable[[DataclassDTOT], None]] = post_init_validator
|
|
138
|
+
) -> None:
|
|
139
|
+
for validator in _validators:
|
|
140
|
+
validator(self)
|
|
141
|
+
|
|
142
|
+
namespace["__post_init__"] = post_init
|
|
143
|
+
|
|
144
|
+
module = __name__
|
|
145
|
+
if model_module := getmodule(model):
|
|
146
|
+
module = model_module.__name__
|
|
147
|
+
|
|
148
|
+
bases = (self.dto_base, base) if base else (self.dto_base,)
|
|
149
|
+
|
|
150
|
+
return dataclasses.make_dataclass(
|
|
151
|
+
name,
|
|
152
|
+
fields=fields,
|
|
153
|
+
bases=bases,
|
|
154
|
+
module=module,
|
|
155
|
+
repr=repr,
|
|
156
|
+
eq=eq,
|
|
157
|
+
order=order,
|
|
158
|
+
unsafe_hash=unsafe_hash,
|
|
159
|
+
frozen=frozen,
|
|
160
|
+
match_args=match_args,
|
|
161
|
+
kw_only=kw_only,
|
|
162
|
+
slots=slots,
|
|
163
|
+
namespace=namespace,
|
|
164
|
+
)
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
"""Pydantic DTO implementation.
|
|
2
|
+
|
|
3
|
+
This module provides classes and utilities for creating Data Transfer Objects (DTOs)
|
|
4
|
+
using Pydantic. It includes base classes for Pydantic DTOs, a backend class
|
|
5
|
+
for generating DTOs from models, and support for mapping DTOs to SQLAlchemy models.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import dataclasses
|
|
11
|
+
from inspect import getmodule
|
|
12
|
+
from typing import TYPE_CHECKING, Annotated, Any, TypeVar, cast, override
|
|
13
|
+
|
|
14
|
+
from pydantic import BaseModel, BeforeValidator, ConfigDict, create_model
|
|
15
|
+
from pydantic.fields import Field, FieldInfo
|
|
16
|
+
from strawchemy.dto.base import DTOBackend, DTOBase, DTOFieldDefinition, MappedDTO, ModelFieldT, ModelT
|
|
17
|
+
from strawchemy.dto.types import DTO_MISSING, DTOMissingType
|
|
18
|
+
|
|
19
|
+
if TYPE_CHECKING:
|
|
20
|
+
from collections.abc import Iterable
|
|
21
|
+
|
|
22
|
+
from pydantic.functional_validators import _V2Validator
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
__all__ = ("MappedPydanticDTO", "PydanticDTO", "PydanticDTOBackend")
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
PydanticDTOT = TypeVar("PydanticDTOT", bound="PydanticDTO[Any] | MappedPydanticDTO[Any]")
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class _PydanticDTOBase(BaseModel):
|
|
32
|
+
model_config = ConfigDict(from_attributes=True, arbitrary_types_allowed=True, defer_build=True)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class PydanticDTO(_PydanticDTOBase, DTOBase[ModelT]): ...
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class MappedPydanticDTO(_PydanticDTOBase, MappedDTO[ModelT]):
|
|
39
|
+
@override
|
|
40
|
+
def to_mapped(self, **override: Any) -> ModelT:
|
|
41
|
+
"""Create an instance of `self.__sqla_model__`.
|
|
42
|
+
|
|
43
|
+
Fill the bound SQLAlchemy model recursively with values from this dataclass.
|
|
44
|
+
"""
|
|
45
|
+
as_model = {}
|
|
46
|
+
dc_fields: dict[str, dataclasses.Field[Any]] = {}
|
|
47
|
+
if dataclasses.is_dataclass(self.__dto_model__):
|
|
48
|
+
dc_fields = {f.name: f for f in dataclasses.fields(self.__dto_model__)}
|
|
49
|
+
|
|
50
|
+
for name, pydantic_field in self.model_fields.items():
|
|
51
|
+
if (value := override.get(name, DTO_MISSING)) and value is not DTO_MISSING:
|
|
52
|
+
as_model[name] = value
|
|
53
|
+
continue
|
|
54
|
+
if (field := dc_fields.get(name, None)) and not field.init:
|
|
55
|
+
continue
|
|
56
|
+
|
|
57
|
+
value: ModelT | MappedPydanticDTO[ModelT] | list[ModelT] | list[MappedPydanticDTO[ModelT]]
|
|
58
|
+
value = getattr(self, name)
|
|
59
|
+
|
|
60
|
+
if isinstance(value, list | tuple):
|
|
61
|
+
value = [el.to_mapped() if isinstance(el, MappedPydanticDTO) else cast(ModelT, el) for el in value]
|
|
62
|
+
if isinstance(value, MappedPydanticDTO):
|
|
63
|
+
value = value.to_mapped()
|
|
64
|
+
as_model[pydantic_field.alias or name] = value
|
|
65
|
+
try:
|
|
66
|
+
return cast("ModelT", self.__dto_model__(**(as_model | override)))
|
|
67
|
+
except TypeError as error:
|
|
68
|
+
original_message = error.args[0] if isinstance(error.args[0], str) else repr(error)
|
|
69
|
+
msg = f"{original_message} (model: {self.__dto_model__.__name__})"
|
|
70
|
+
raise TypeError(msg) from error
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class PydanticDTOBackend(DTOBackend[PydanticDTOT]):
|
|
74
|
+
"""Implements DTO factory using pydantic."""
|
|
75
|
+
|
|
76
|
+
def __init__(self, dto_base: type[PydanticDTOT]) -> None:
|
|
77
|
+
self.dto_base = dto_base
|
|
78
|
+
|
|
79
|
+
def _construct_field_info(self, field_def: DTOFieldDefinition[ModelT, ModelFieldT]) -> FieldInfo:
|
|
80
|
+
"""Build a `FieldInfo instance reflecting the given field_def."""
|
|
81
|
+
kwargs: dict[str, Any] = {}
|
|
82
|
+
if field_def.required:
|
|
83
|
+
kwargs["default"] = ...
|
|
84
|
+
elif not isinstance(field_def.default_factory, DTOMissingType):
|
|
85
|
+
kwargs["default_factory"] = field_def.default_factory
|
|
86
|
+
elif not isinstance(field_def.default, DTOMissingType):
|
|
87
|
+
kwargs["default"] = field_def.default
|
|
88
|
+
if field_def.purpose_config.alias:
|
|
89
|
+
kwargs["alias"] = field_def.model_field_name
|
|
90
|
+
return Field(**kwargs)
|
|
91
|
+
|
|
92
|
+
@override
|
|
93
|
+
def update_forward_refs(self, dto: type[PydanticDTOT], namespace: dict[str, type[PydanticDTOT]]) -> None:
|
|
94
|
+
dto.model_rebuild(_types_namespace=namespace, raise_errors=False)
|
|
95
|
+
|
|
96
|
+
@override
|
|
97
|
+
def build(
|
|
98
|
+
self,
|
|
99
|
+
name: str,
|
|
100
|
+
model: type[ModelT],
|
|
101
|
+
field_definitions: Iterable[DTOFieldDefinition[ModelT, ModelFieldT]],
|
|
102
|
+
base: type[Any] | None = None,
|
|
103
|
+
config_dict: ConfigDict | None = None,
|
|
104
|
+
docstring: bool = True,
|
|
105
|
+
**kwargs: Any,
|
|
106
|
+
) -> type[PydanticDTOT]:
|
|
107
|
+
fields: dict[str, tuple[Any, FieldInfo]] = {}
|
|
108
|
+
validators: dict[str, _V2Validator] = {}
|
|
109
|
+
base_annotations = base.__annotations__ if base else {}
|
|
110
|
+
|
|
111
|
+
for field_def in field_definitions:
|
|
112
|
+
field_type = field_def.type_
|
|
113
|
+
validator: BeforeValidator | None = None
|
|
114
|
+
if field_def.purpose_config.validator:
|
|
115
|
+
validator = BeforeValidator(field_def.purpose_config.validator)
|
|
116
|
+
if validator:
|
|
117
|
+
field_type = Annotated[field_type, validator]
|
|
118
|
+
fields[field_def.name] = (field_type, self._construct_field_info(field_def))
|
|
119
|
+
|
|
120
|
+
# Copy fields from base to avoid Pydantic warning about shadowing fields
|
|
121
|
+
for f_name in base_annotations:
|
|
122
|
+
field_info: FieldInfo = Field()
|
|
123
|
+
attribute = getattr(base, f_name, DTO_MISSING)
|
|
124
|
+
if not isinstance(attribute, DTOMissingType):
|
|
125
|
+
field_info = attribute if isinstance(attribute, FieldInfo) else Field(default=attribute)
|
|
126
|
+
field_type = fields[f_name][0] if f_name in fields else base_annotations[f_name]
|
|
127
|
+
fields[f_name] = (field_type, field_info)
|
|
128
|
+
|
|
129
|
+
module = __name__
|
|
130
|
+
if model_module := getmodule(model):
|
|
131
|
+
module = model_module.__name__
|
|
132
|
+
|
|
133
|
+
dto = create_model(
|
|
134
|
+
name,
|
|
135
|
+
__base__=(self.dto_base,),
|
|
136
|
+
__config__=None,
|
|
137
|
+
__module__=module,
|
|
138
|
+
__validators__=validators,
|
|
139
|
+
__doc__=f"Pydantic generated DTO for {model.__name__} model" if docstring else None,
|
|
140
|
+
__cls_kwargs__=None,
|
|
141
|
+
**fields,
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
if config_dict:
|
|
145
|
+
cls_body = {"model_config": config_dict} if config_dict else {}
|
|
146
|
+
return type(dto.__name__, (dto,), cls_body)
|
|
147
|
+
return dto
|