arpakitlib 1.8.257__py3-none-any.whl → 1.8.259__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.
- arpakitlib/ar_pydantic_schema_from_sqlalchemy_model.py +52 -68
- {arpakitlib-1.8.257.dist-info → arpakitlib-1.8.259.dist-info}/METADATA +1 -1
- {arpakitlib-1.8.257.dist-info → arpakitlib-1.8.259.dist-info}/RECORD +6 -6
- {arpakitlib-1.8.257.dist-info → arpakitlib-1.8.259.dist-info}/LICENSE +0 -0
- {arpakitlib-1.8.257.dist-info → arpakitlib-1.8.259.dist-info}/WHEEL +0 -0
- {arpakitlib-1.8.257.dist-info → arpakitlib-1.8.259.dist-info}/entry_points.txt +0 -0
@@ -1,48 +1,41 @@
|
|
1
1
|
# arpakit
|
2
|
-
import
|
3
|
-
from typing import Any, Optional, get_type_hints, get_origin, Union, Annotated, get_args
|
2
|
+
from typing import Any, get_type_hints, get_origin, Union, Annotated, get_args
|
4
3
|
|
5
|
-
from pydantic import BaseModel,
|
4
|
+
from pydantic import BaseModel, ConfigDict
|
6
5
|
from sqlalchemy import inspect
|
7
|
-
from sqlalchemy.orm import ColumnProperty
|
8
|
-
from sqlalchemy.
|
9
|
-
|
10
|
-
|
11
|
-
DateTime, Date, Time,
|
12
|
-
Float, Numeric, DECIMAL, LargeBinary, JSON
|
13
|
-
)
|
6
|
+
from sqlalchemy.orm import ColumnProperty, Mapped
|
7
|
+
from sqlalchemy.util import get_annotations
|
8
|
+
|
9
|
+
from project.sqlalchemy_db_.sqlalchemy_model import UserDBM
|
14
10
|
|
15
11
|
_ARPAKIT_LIB_MODULE_VERSION = "3.0"
|
16
12
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
Text: str,
|
28
|
-
UnicodeText: str,
|
29
|
-
LargeBinary: bytes,
|
30
|
-
JSON: dict | list,
|
31
|
-
DateTime: dt.datetime,
|
32
|
-
Date: dt.date,
|
33
|
-
Time: dt.time,
|
34
|
-
}
|
35
|
-
|
36
|
-
|
37
|
-
def _python_type_from_col(col) -> type | str:
|
38
|
-
try:
|
39
|
-
return col.type.python_type
|
40
|
-
except Exception:
|
41
|
-
for sa_t, py_t in _SQLA_TYPE_MAP.items():
|
42
|
-
if isinstance(col.type, sa_t):
|
43
|
-
return py_t
|
13
|
+
|
14
|
+
def __declared_sqlalchemy_column_type(declared_type: Any) -> Any:
|
15
|
+
"""
|
16
|
+
Возвращает тип колонки ИСКЛЮЧИТЕЛЬНО из аннотации поля.
|
17
|
+
Разворачивает оболочки:
|
18
|
+
- Annotated[T, ...] -> T
|
19
|
+
- Mapped[T] -> T
|
20
|
+
Если аннотации нет — возвращает Any.
|
21
|
+
"""
|
22
|
+
if declared_type is None or declared_type is Any:
|
44
23
|
return Any
|
45
24
|
|
25
|
+
origin = get_origin(declared_type)
|
26
|
+
|
27
|
+
# Annotated[T, ...] -> T
|
28
|
+
if origin is Annotated:
|
29
|
+
args = get_args(declared_type)
|
30
|
+
return __declared_sqlalchemy_column_type(args[0]) if args else Any
|
31
|
+
|
32
|
+
# Mapped[T] -> T
|
33
|
+
if origin is Mapped:
|
34
|
+
args = get_args(declared_type)
|
35
|
+
return __declared_sqlalchemy_column_type(args[0]) if args else Any
|
36
|
+
|
37
|
+
return declared_type
|
38
|
+
|
46
39
|
|
47
40
|
def _get_property_name_to_type_from_model_class(
|
48
41
|
*,
|
@@ -64,8 +57,9 @@ def _get_property_name_to_type_from_model_class(
|
|
64
57
|
ret_type = hints.get("return", Any)
|
65
58
|
except Exception:
|
66
59
|
if skip_property_if_cannot_define_type:
|
67
|
-
|
68
|
-
|
60
|
+
ret_type = Any
|
61
|
+
else:
|
62
|
+
raise
|
69
63
|
if exclude_property_names:
|
70
64
|
if property_name in exclude_property_names:
|
71
65
|
continue
|
@@ -113,7 +107,7 @@ def pydantic_schema_from_sqlalchemy_model(
|
|
113
107
|
*,
|
114
108
|
model_name: str | None = None,
|
115
109
|
base_model: type[BaseModel] = BaseModel,
|
116
|
-
|
110
|
+
include_columns: bool = True,
|
117
111
|
exclude_column_names: list[str] | None = None,
|
118
112
|
include_properties: bool = False,
|
119
113
|
include_property_names: list[str] | None = None,
|
@@ -122,7 +116,8 @@ def pydantic_schema_from_sqlalchemy_model(
|
|
122
116
|
exclude_property_types: list[type] | None = None,
|
123
117
|
filter_property_prefixes: list[str] | None = None,
|
124
118
|
remove_property_prefixes: list[str] | None = None,
|
125
|
-
skip_property_if_cannot_define_type: bool = True
|
119
|
+
skip_property_if_cannot_define_type: bool = True,
|
120
|
+
skip_property_name_if_exists: bool = True
|
126
121
|
) -> type[BaseModel]:
|
127
122
|
"""
|
128
123
|
Генерирует Pydantic-модель из колонок SQLAlchemy-модели и (опционально) из @property.
|
@@ -147,31 +142,18 @@ def pydantic_schema_from_sqlalchemy_model(
|
|
147
142
|
remove_property_prefixes = set(remove_property_prefixes or [])
|
148
143
|
|
149
144
|
# 1) Колонки
|
150
|
-
|
151
|
-
if not isinstance(prop, ColumnProperty):
|
152
|
-
continue
|
153
|
-
if prop.key in exclude_column_names:
|
154
|
-
continue
|
155
|
-
|
156
|
-
column = prop.columns[0]
|
157
|
-
column_type = _python_type_from_col(column)
|
158
|
-
|
159
|
-
# Аннотация типа
|
160
|
-
if column.nullable:
|
161
|
-
annotations[prop.key] = Optional[column_type] # type: ignore[name-defined]
|
162
|
-
else:
|
163
|
-
annotations[prop.key] = column_type
|
145
|
+
if include_columns:
|
164
146
|
|
165
|
-
#
|
166
|
-
|
167
|
-
default_value = None
|
168
|
-
if column.default is not None and getattr(column.default, "is_scalar", False):
|
169
|
-
default_value = column.default.arg
|
170
|
-
elif column.server_default is not None and getattr(column.server_default.arg, "text", None):
|
171
|
-
default_value = column.server_default.arg.text
|
147
|
+
# читаем аннотации класса (include_extras=True нужно для Annotated/Mapped)
|
148
|
+
type_hints = get_annotations(sqlalchemy_model)
|
172
149
|
|
173
|
-
|
174
|
-
|
150
|
+
for prop in mapper.column_attrs:
|
151
|
+
if not isinstance(prop, ColumnProperty):
|
152
|
+
continue
|
153
|
+
if prop.key in exclude_column_names:
|
154
|
+
continue
|
155
|
+
|
156
|
+
annotations[prop.key] = __declared_sqlalchemy_column_type(type_hints.get(prop.key, None))
|
175
157
|
|
176
158
|
# 2) Свойства (@property)
|
177
159
|
if include_properties:
|
@@ -240,10 +222,11 @@ def pydantic_schema_from_sqlalchemy_model(
|
|
240
222
|
):
|
241
223
|
renamed_property_name_to_type[new_property_name] = property_type
|
242
224
|
else:
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
225
|
+
if not skip_property_name_if_exists:
|
226
|
+
raise ValueError(
|
227
|
+
f"Property name '{property_name}' after removing prefix "
|
228
|
+
f"conflicts with existing name '{new_property_name}'"
|
229
|
+
)
|
247
230
|
property_name_to_type.update(renamed_property_name_to_type)
|
248
231
|
|
249
232
|
# добавляем (колонки в приоритете)
|
@@ -258,3 +241,4 @@ def pydantic_schema_from_sqlalchemy_model(
|
|
258
241
|
attrs["model_config"] = ConfigDict(extra="ignore", arbitrary_types_allowed=True, from_attributes=True)
|
259
242
|
|
260
243
|
return type(model_name, (base_model,), attrs)
|
244
|
+
|
@@ -414,7 +414,7 @@ arpakitlib/ar_need_type_util.py,sha256=XmY1kswz8j9oo5f9CxRu0_zgfvxWrXPYKOj6MM04s
|
|
414
414
|
arpakitlib/ar_openai_api_client_util.py,sha256=dWgsSPXtxNBxS5VRi_NharGQrUXF_YjIfhU3Bj5cW9M,5651
|
415
415
|
arpakitlib/ar_parse_command.py,sha256=1WTdQoWVshoDZ1jDaKeTzajfqaYHP3FNO0-REyo1aMY,3003
|
416
416
|
arpakitlib/ar_postgresql_util.py,sha256=1AuLjEaa1Lg4pzn-ukCVnDi35Eg1k91APRTqZhIJAdo,945
|
417
|
-
arpakitlib/ar_pydantic_schema_from_sqlalchemy_model.py,sha256=
|
417
|
+
arpakitlib/ar_pydantic_schema_from_sqlalchemy_model.py,sha256=MBIP8VDohDB5Z3awbhJehNNRt0xIO4s6sEBezsPHP3s,10413
|
418
418
|
arpakitlib/ar_raise_own_exception_if_exception.py,sha256=A6TuNSBk1pHaQ_qxnUmE2LgsNGA1IGqX26b1_HEA4Nc,5978
|
419
419
|
arpakitlib/ar_rat_func_util.py,sha256=Ca10o3RJwyx_DJLxjTxgHDO6NU3M6CWgUR4bif67OE4,2006
|
420
420
|
arpakitlib/ar_really_validate_email.py,sha256=HBfhyiDB3INI6Iq6hR2WOMKA5wVWWRl0Qun-x__OZ9o,1201
|
@@ -430,8 +430,8 @@ arpakitlib/ar_sqlalchemy_util.py,sha256=FDva9onjtCPrYZYIHHb93NMwD1WlmaORjiWgPRJQ
|
|
430
430
|
arpakitlib/ar_str_util.py,sha256=2lGpnXDf2h1cBZpVf5i1tX_HCv5iBd6IGnrCw4QWWlY,4350
|
431
431
|
arpakitlib/ar_type_util.py,sha256=Cs_tef-Fc5xeyAF54KgISCsP11NHyzIsglm4S3Xx7iM,4049
|
432
432
|
arpakitlib/ar_yookassa_api_client_util.py,sha256=VozuZeCJjmLd1zj2BdC9WfiAQ3XYOrIMsdpNK-AUlm0,5347
|
433
|
-
arpakitlib-1.8.
|
434
|
-
arpakitlib-1.8.
|
435
|
-
arpakitlib-1.8.
|
436
|
-
arpakitlib-1.8.
|
437
|
-
arpakitlib-1.8.
|
433
|
+
arpakitlib-1.8.259.dist-info/LICENSE,sha256=GPEDQMam2r7FSTYqM1mm7aKnxLaWcBotH7UvQtea-ec,11355
|
434
|
+
arpakitlib-1.8.259.dist-info/METADATA,sha256=GEDZNXDfp08rmbipsZhX4WwXtmlOGrJbJEpIJ7Q-KDg,3919
|
435
|
+
arpakitlib-1.8.259.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
436
|
+
arpakitlib-1.8.259.dist-info/entry_points.txt,sha256=36xqR3PJFT2kuwjkM_EqoIy0qFUDPKSm_mJaI7emewE,87
|
437
|
+
arpakitlib-1.8.259.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|