arpakitlib 1.8.250__py3-none-any.whl → 1.8.251__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 +71 -15
- {arpakitlib-1.8.250.dist-info → arpakitlib-1.8.251.dist-info}/METADATA +1 -1
- {arpakitlib-1.8.250.dist-info → arpakitlib-1.8.251.dist-info}/RECORD +6 -6
- {arpakitlib-1.8.250.dist-info → arpakitlib-1.8.251.dist-info}/LICENSE +0 -0
- {arpakitlib-1.8.250.dist-info → arpakitlib-1.8.251.dist-info}/WHEEL +0 -0
- {arpakitlib-1.8.250.dist-info → arpakitlib-1.8.251.dist-info}/entry_points.txt +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
# arpakit
|
2
2
|
import datetime as dt
|
3
|
-
from typing import Any, Optional
|
3
|
+
from typing import Any, Optional, get_type_hints
|
4
4
|
|
5
5
|
from pydantic import BaseModel, Field
|
6
6
|
from sqlalchemy import inspect
|
@@ -11,6 +11,7 @@ from sqlalchemy.sql.sqltypes import (
|
|
11
11
|
DateTime, Date, Time,
|
12
12
|
Float, Numeric, DECIMAL, LargeBinary, JSON
|
13
13
|
)
|
14
|
+
|
14
15
|
_ARPAKIT_LIB_MODULE_VERSION = "3.0"
|
15
16
|
|
16
17
|
_SQLA_TYPE_MAP = {
|
@@ -43,51 +44,106 @@ def _python_type_from_col(col) -> type | str:
|
|
43
44
|
return Any
|
44
45
|
|
45
46
|
|
47
|
+
def _collect_properties_with_types(model_class: type) -> dict[str, Any]:
|
48
|
+
"""
|
49
|
+
Находит все @property в классе и вытаскивает их возвращаемый тип.
|
50
|
+
Если тип не удаётся получить — подставляем Any.
|
51
|
+
"""
|
52
|
+
props: dict[str, Any] = {}
|
53
|
+
for name, attr in vars(model_class).items():
|
54
|
+
if isinstance(attr, property):
|
55
|
+
try:
|
56
|
+
hints = get_type_hints(attr.fget) if attr.fget else {}
|
57
|
+
ret_type = hints.get("return", Any)
|
58
|
+
except Exception:
|
59
|
+
ret_type = Any
|
60
|
+
props[name] = ret_type
|
61
|
+
return props
|
62
|
+
|
63
|
+
|
46
64
|
def pydantic_schema_from_sqlalchemy_model(
|
47
65
|
sqlalchemy_model: type,
|
48
66
|
*,
|
49
67
|
name: str | None = None,
|
50
68
|
base_model: type[BaseModel] = BaseModel,
|
51
|
-
|
69
|
+
include_column_defaults: bool = False,
|
52
70
|
exclude_column_names: list[str] | None = None,
|
71
|
+
include_properties: bool = False,
|
72
|
+
include_property_names: list[str] | None = None,
|
73
|
+
exclude_property_names: list[str] | None = None,
|
53
74
|
) -> type[BaseModel]:
|
54
75
|
"""
|
55
|
-
Генерирует Pydantic-модель
|
56
|
-
|
76
|
+
Генерирует Pydantic-модель из колонок SQLAlchemy-модели и (опционально) из @property.
|
77
|
+
|
78
|
+
- include_column_defaults: добавлять ли default/server_default у колонок.
|
57
79
|
- exclude_column_names: список имён колонок, которые нужно пропустить.
|
80
|
+
|
81
|
+
- include_properties: включать ли свойства (@property). По умолчанию False.
|
82
|
+
- include_property_names: whitelist имён свойств (если задан, берём только их).
|
83
|
+
- exclude_property_names: blacklist имён свойств (исключаются после whitelist'а).
|
58
84
|
"""
|
59
85
|
mapper = inspect(sqlalchemy_model).mapper
|
60
86
|
model_name = name or f"{sqlalchemy_model.__name__}Schema"
|
61
87
|
|
62
88
|
annotations: dict[str, Any] = {}
|
63
89
|
attrs: dict[str, Any] = {}
|
90
|
+
|
64
91
|
exclude_column_names = set(exclude_column_names or [])
|
92
|
+
include_property_names = set(include_property_names or [])
|
93
|
+
exclude_property_names = set(exclude_property_names or [])
|
65
94
|
|
95
|
+
# 1) Колонки
|
66
96
|
for prop in mapper.attrs:
|
67
97
|
if not isinstance(prop, ColumnProperty):
|
68
98
|
continue
|
69
99
|
if prop.key in exclude_column_names:
|
70
100
|
continue
|
71
101
|
|
72
|
-
|
73
|
-
|
102
|
+
column = prop.columns[0]
|
103
|
+
column_type = _python_type_from_col(column)
|
74
104
|
|
75
105
|
# Аннотация типа
|
76
|
-
if
|
77
|
-
annotations[prop.key] = Optional[
|
106
|
+
if column.nullable:
|
107
|
+
annotations[prop.key] = Optional[column_type] # type: ignore[name-defined]
|
78
108
|
else:
|
79
|
-
annotations[prop.key] =
|
109
|
+
annotations[prop.key] = column_type
|
80
110
|
|
81
|
-
#
|
82
|
-
if
|
111
|
+
# Дефолты, если нужно
|
112
|
+
if include_column_defaults:
|
83
113
|
default_value = None
|
84
|
-
if
|
85
|
-
default_value =
|
86
|
-
elif
|
87
|
-
default_value =
|
114
|
+
if column.default is not None and getattr(column.default, "is_scalar", False):
|
115
|
+
default_value = column.default.arg
|
116
|
+
elif column.server_default is not None and getattr(column.server_default.arg, "text", None):
|
117
|
+
default_value = column.server_default.arg.text
|
88
118
|
|
89
119
|
if default_value is not None:
|
90
120
|
attrs[prop.key] = Field(default=default_value)
|
91
121
|
|
122
|
+
# 2) Свойства (@property)
|
123
|
+
if include_properties:
|
124
|
+
property_name_to_type = _collect_properties_with_types(sqlalchemy_model)
|
125
|
+
|
126
|
+
# whitelist (если задан)
|
127
|
+
if include_property_names:
|
128
|
+
property_name_to_type = {
|
129
|
+
k: v
|
130
|
+
for k, v in property_name_to_type.items()
|
131
|
+
if k in include_property_names
|
132
|
+
}
|
133
|
+
else:
|
134
|
+
property_name_to_type = dict(property_name_to_type)
|
135
|
+
|
136
|
+
# blacklist
|
137
|
+
if exclude_property_names:
|
138
|
+
for property_name in list(property_name_to_type.keys()):
|
139
|
+
if property_name in exclude_property_names:
|
140
|
+
property_name_to_type.pop(name, None)
|
141
|
+
|
142
|
+
# Добавляем аннотации свойств (колонки имеют приоритет)
|
143
|
+
for property_name, property_type in property_name_to_type.items():
|
144
|
+
if property_name in annotations:
|
145
|
+
continue
|
146
|
+
annotations[property_name] = property_type
|
147
|
+
|
92
148
|
attrs["__annotations__"] = annotations
|
93
149
|
return type(model_name, (base_model,), attrs)
|
@@ -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=NuvydQe7Cih6dW5H53GB6BsG2oFKgf27F3iJxuPO2J0,5364
|
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=5I7KsKCzJc9hxpn2_0r__RcbqcKpTs_PDw_f5_0P
|
|
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.251.dist-info/LICENSE,sha256=GPEDQMam2r7FSTYqM1mm7aKnxLaWcBotH7UvQtea-ec,11355
|
434
|
+
arpakitlib-1.8.251.dist-info/METADATA,sha256=qCRaVdN1qqbRnIqYG7bYL2J3i6_sDOIpxqO7SG1BLNA,3919
|
435
|
+
arpakitlib-1.8.251.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
436
|
+
arpakitlib-1.8.251.dist-info/entry_points.txt,sha256=36xqR3PJFT2kuwjkM_EqoIy0qFUDPKSm_mJaI7emewE,87
|
437
|
+
arpakitlib-1.8.251.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|