sqlobjects 0.1.0__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.
- sqlobjects/__init__.py +38 -0
- sqlobjects/config.py +519 -0
- sqlobjects/database.py +586 -0
- sqlobjects/exceptions.py +538 -0
- sqlobjects/expressions.py +1054 -0
- sqlobjects/fields.py +1866 -0
- sqlobjects/history.py +101 -0
- sqlobjects/metadata.py +1130 -0
- sqlobjects/model.py +1009 -0
- sqlobjects/objects.py +812 -0
- sqlobjects/queries.py +1059 -0
- sqlobjects/relations.py +843 -0
- sqlobjects/session.py +389 -0
- sqlobjects/signals.py +464 -0
- sqlobjects/utils/__init__.py +5 -0
- sqlobjects/utils/naming.py +53 -0
- sqlobjects/utils/pattern.py +644 -0
- sqlobjects/validators.py +294 -0
- sqlobjects-0.1.0.dist-info/METADATA +29 -0
- sqlobjects-0.1.0.dist-info/RECORD +23 -0
- sqlobjects-0.1.0.dist-info/WHEEL +5 -0
- sqlobjects-0.1.0.dist-info/licenses/LICENSE +21 -0
- sqlobjects-0.1.0.dist-info/top_level.txt +1 -0
sqlobjects/fields.py
ADDED
|
@@ -0,0 +1,1866 @@
|
|
|
1
|
+
"""SQLObjects Fields Module - Enhanced SQLAlchemy Core types with function chaining"""
|
|
2
|
+
|
|
3
|
+
import builtins
|
|
4
|
+
import inspect
|
|
5
|
+
from collections.abc import Callable
|
|
6
|
+
from functools import lru_cache
|
|
7
|
+
from typing import TYPE_CHECKING, Any, Generic, NotRequired, Type, TypedDict, TypeVar, overload
|
|
8
|
+
|
|
9
|
+
from sqlalchemy import Column as CoreColumn
|
|
10
|
+
from sqlalchemy import func
|
|
11
|
+
from sqlalchemy.sql.elements import ColumnElement
|
|
12
|
+
from sqlalchemy.sql.sqltypes import (
|
|
13
|
+
ARRAY,
|
|
14
|
+
JSON,
|
|
15
|
+
BigInteger,
|
|
16
|
+
Boolean,
|
|
17
|
+
Date,
|
|
18
|
+
DateTime,
|
|
19
|
+
Double,
|
|
20
|
+
Enum,
|
|
21
|
+
Float,
|
|
22
|
+
Integer,
|
|
23
|
+
Interval,
|
|
24
|
+
LargeBinary,
|
|
25
|
+
Numeric,
|
|
26
|
+
SmallInteger,
|
|
27
|
+
String,
|
|
28
|
+
Text,
|
|
29
|
+
Time,
|
|
30
|
+
TypeEngine,
|
|
31
|
+
Uuid,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
from .expressions import (
|
|
35
|
+
DateTimeFunctionMixin,
|
|
36
|
+
FunctionExpression,
|
|
37
|
+
FunctionMixin,
|
|
38
|
+
NumericFunctionMixin,
|
|
39
|
+
StringFunctionMixin,
|
|
40
|
+
)
|
|
41
|
+
from .relations import relationship
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
if TYPE_CHECKING:
|
|
45
|
+
from sqlalchemy.sql.sqltypes import TypeEngine # noqa
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
__all__ = [
|
|
49
|
+
# Core
|
|
50
|
+
"Column",
|
|
51
|
+
"column",
|
|
52
|
+
"ColumnAttribute",
|
|
53
|
+
"Auto",
|
|
54
|
+
# Type system
|
|
55
|
+
"register_field_type",
|
|
56
|
+
"create_type_instance",
|
|
57
|
+
"get_type_definition",
|
|
58
|
+
# Shortcut functions
|
|
59
|
+
"str_column",
|
|
60
|
+
"int_column",
|
|
61
|
+
"bool_column",
|
|
62
|
+
"json_column",
|
|
63
|
+
# shortcuts
|
|
64
|
+
"datetime_column",
|
|
65
|
+
"numeric_column",
|
|
66
|
+
"text_column",
|
|
67
|
+
"binary_column",
|
|
68
|
+
# Relationship fields
|
|
69
|
+
"relationship",
|
|
70
|
+
# Validation utilities
|
|
71
|
+
"get_field_validators",
|
|
72
|
+
"get_model_metadata",
|
|
73
|
+
]
|
|
74
|
+
|
|
75
|
+
# === Type System ===
|
|
76
|
+
|
|
77
|
+
T = TypeVar("T")
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class TypeArgument(TypedDict):
|
|
81
|
+
name: str
|
|
82
|
+
type: type[Any]
|
|
83
|
+
required: bool
|
|
84
|
+
default: Any
|
|
85
|
+
transform: NotRequired[Callable[[Any], Any]]
|
|
86
|
+
positional: NotRequired[bool]
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class TypeDefinition(TypedDict):
|
|
90
|
+
type: type[Any]
|
|
91
|
+
arguments: list[TypeArgument]
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def _transform_array_item_type(item_type: str | type[Any]) -> type[Any]:
|
|
95
|
+
"""Transform array item_type from string to SQLAlchemy type."""
|
|
96
|
+
if isinstance(item_type, str):
|
|
97
|
+
type_def = _registry.get_type(item_type)
|
|
98
|
+
if type_def:
|
|
99
|
+
return type_def["type"]()
|
|
100
|
+
else:
|
|
101
|
+
raise ValueError(f"Unknown array item type: {item_type}")
|
|
102
|
+
return item_type
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def _extract_constructor_params(type_class: type[Any]) -> list[TypeArgument]:
|
|
106
|
+
"""Extract constructor parameters using inspect."""
|
|
107
|
+
try:
|
|
108
|
+
sig = inspect.signature(type_class.__init__)
|
|
109
|
+
arguments = []
|
|
110
|
+
for param_name, param in sig.parameters.items():
|
|
111
|
+
if param_name == "self":
|
|
112
|
+
continue
|
|
113
|
+
arguments.append(
|
|
114
|
+
{
|
|
115
|
+
"name": param_name,
|
|
116
|
+
"type": Any,
|
|
117
|
+
"required": param.default == inspect.Parameter.empty,
|
|
118
|
+
"default": param.default if param.default != inspect.Parameter.empty else None,
|
|
119
|
+
}
|
|
120
|
+
)
|
|
121
|
+
return arguments
|
|
122
|
+
except Exception: # noqa
|
|
123
|
+
return []
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def _get_type_params(type_def: TypeDefinition, kwargs: dict[str, Any]) -> dict[str, Any]:
|
|
127
|
+
"""Extract type construction parameters from kwargs
|
|
128
|
+
|
|
129
|
+
Args:
|
|
130
|
+
type_def: Type definition containing parameter specifications
|
|
131
|
+
kwargs: Keyword arguments to extract from
|
|
132
|
+
|
|
133
|
+
Returns:
|
|
134
|
+
Dictionary of type parameters with transformed values
|
|
135
|
+
"""
|
|
136
|
+
type_params = {}
|
|
137
|
+
type_param_names = {arg["name"] for arg in type_def["arguments"]}
|
|
138
|
+
|
|
139
|
+
for key, value in kwargs.items():
|
|
140
|
+
if key in type_param_names:
|
|
141
|
+
arg_def = next(arg for arg in type_def["arguments"] if arg["name"] == key)
|
|
142
|
+
if "transform" in arg_def and arg_def["transform"]:
|
|
143
|
+
value = arg_def["transform"](value)
|
|
144
|
+
type_params[key] = value
|
|
145
|
+
|
|
146
|
+
# Apply default values
|
|
147
|
+
for arg in type_def["arguments"]:
|
|
148
|
+
if arg["name"] not in type_params and not arg["required"] and arg["default"] is not None:
|
|
149
|
+
default_value = arg["default"]
|
|
150
|
+
if "transform" in arg and arg["transform"]:
|
|
151
|
+
default_value = arg["transform"](default_value)
|
|
152
|
+
type_params[arg["name"]] = default_value
|
|
153
|
+
|
|
154
|
+
return type_params
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
class TypeRegistry:
|
|
158
|
+
"""Type registry with caching and lazy loading support
|
|
159
|
+
|
|
160
|
+
Manages registration and lookup of field types with LRU caching
|
|
161
|
+
for performance optimization.
|
|
162
|
+
"""
|
|
163
|
+
|
|
164
|
+
def __init__(self):
|
|
165
|
+
self._types: dict[str, TypeDefinition] = {}
|
|
166
|
+
self._aliases: dict[str, str] = {}
|
|
167
|
+
self._initialized = False
|
|
168
|
+
|
|
169
|
+
@lru_cache(maxsize=128) # noqa: B019
|
|
170
|
+
def get_type(self, name: str) -> TypeDefinition:
|
|
171
|
+
"""Cached type lookup with lazy initialization
|
|
172
|
+
|
|
173
|
+
Args:
|
|
174
|
+
name: Type name or alias to look up
|
|
175
|
+
|
|
176
|
+
Returns:
|
|
177
|
+
Type definition for the requested type
|
|
178
|
+
|
|
179
|
+
Raises:
|
|
180
|
+
ValueError: If type is not found
|
|
181
|
+
"""
|
|
182
|
+
if not self._initialized:
|
|
183
|
+
self._init_builtin_types()
|
|
184
|
+
|
|
185
|
+
type_def = self._types.get(self._resolve_alias(name))
|
|
186
|
+
if not type_def:
|
|
187
|
+
available_types = list(self._types.keys())
|
|
188
|
+
raise ValueError(f"Unknown type: '{name}'. Available types: {available_types}")
|
|
189
|
+
|
|
190
|
+
return type_def
|
|
191
|
+
|
|
192
|
+
def register(self, type_def: TypeDefinition | type[Any], type_name: str | None, aliases: list[str] | None = None):
|
|
193
|
+
"""Register a type with optional aliases
|
|
194
|
+
|
|
195
|
+
Args:
|
|
196
|
+
type_def: Type definition or type class to register
|
|
197
|
+
type_name: Name to register the type under
|
|
198
|
+
aliases: Optional list of alias names
|
|
199
|
+
"""
|
|
200
|
+
if isinstance(type_def, type):
|
|
201
|
+
type_def = self._create_type_definition(type_def)
|
|
202
|
+
|
|
203
|
+
name = type_name or type_def["type"].__name__.lower()
|
|
204
|
+
|
|
205
|
+
self._types[name] = type_def
|
|
206
|
+
|
|
207
|
+
if aliases:
|
|
208
|
+
for alias in aliases:
|
|
209
|
+
self._aliases[alias] = name
|
|
210
|
+
|
|
211
|
+
def create_type_instance(self, type_name: str, type_params: dict[str, Any]):
|
|
212
|
+
"""Create type instance with parameters
|
|
213
|
+
|
|
214
|
+
Args:
|
|
215
|
+
type_name: Name of the type to create
|
|
216
|
+
type_params: Parameters for type construction
|
|
217
|
+
|
|
218
|
+
Returns:
|
|
219
|
+
Instantiated SQLAlchemy type object
|
|
220
|
+
"""
|
|
221
|
+
type_def = self.get_type(type_name)
|
|
222
|
+
|
|
223
|
+
positional_args = []
|
|
224
|
+
keyword_args = {}
|
|
225
|
+
|
|
226
|
+
for arg_def in type_def["arguments"]:
|
|
227
|
+
param_name = arg_def["name"]
|
|
228
|
+
if param_name not in type_params:
|
|
229
|
+
continue
|
|
230
|
+
|
|
231
|
+
value = type_params[param_name]
|
|
232
|
+
|
|
233
|
+
# Check if it's a positional parameter (default False)
|
|
234
|
+
if arg_def.get("positional", False):
|
|
235
|
+
positional_args.append(value)
|
|
236
|
+
else:
|
|
237
|
+
keyword_args[param_name] = value
|
|
238
|
+
|
|
239
|
+
return type_def["type"](*positional_args, **keyword_args)
|
|
240
|
+
|
|
241
|
+
def _create_type_definition(self, type_class: Type[Any]) -> TypeDefinition: # noqa
|
|
242
|
+
"""Create type definition from type class
|
|
243
|
+
|
|
244
|
+
Args:
|
|
245
|
+
type_class: Type class to analyze
|
|
246
|
+
|
|
247
|
+
Returns:
|
|
248
|
+
Type definition with extracted parameters
|
|
249
|
+
"""
|
|
250
|
+
try:
|
|
251
|
+
arguments = _extract_constructor_params(type_class)
|
|
252
|
+
return {"type": type_class, "arguments": arguments}
|
|
253
|
+
except Exception: # noqa
|
|
254
|
+
return {"type": type_class, "arguments": []}
|
|
255
|
+
|
|
256
|
+
def _resolve_alias(self, name: str) -> str:
|
|
257
|
+
"""Resolve type alias to actual type name
|
|
258
|
+
|
|
259
|
+
Args:
|
|
260
|
+
name: Type name or alias
|
|
261
|
+
|
|
262
|
+
Returns:
|
|
263
|
+
Actual type name
|
|
264
|
+
"""
|
|
265
|
+
return self._aliases.get(name, name)
|
|
266
|
+
|
|
267
|
+
def _init_builtin_types(self):
|
|
268
|
+
"""Initialize all built-in enhanced types
|
|
269
|
+
|
|
270
|
+
Registers all standard SQLAlchemy types with enhanced functionality.
|
|
271
|
+
"""
|
|
272
|
+
builtin_types = [
|
|
273
|
+
# Auto type (automatic inference)
|
|
274
|
+
(Auto, "auto", []),
|
|
275
|
+
# String abstract types
|
|
276
|
+
(EnhancedString, "string", ["str"]),
|
|
277
|
+
(EnhancedText, "text"),
|
|
278
|
+
# Numeric abstract types
|
|
279
|
+
(EnhancedInteger, "integer", ["int"]),
|
|
280
|
+
(EnhancedBigInteger, "bigint"),
|
|
281
|
+
(EnhancedSmallInteger, "smallint"),
|
|
282
|
+
(EnhancedFloat, "float"),
|
|
283
|
+
(EnhancedDouble, "double"),
|
|
284
|
+
(EnhancedNumeric, "numeric", ["decimal"]),
|
|
285
|
+
# Boolean abstract types
|
|
286
|
+
(EnhancedBoolean, "boolean", ["bool"]),
|
|
287
|
+
# Date/time abstract types
|
|
288
|
+
(EnhancedDate, "date"),
|
|
289
|
+
(EnhancedDateTime, "datetime"),
|
|
290
|
+
(EnhancedTime, "time"),
|
|
291
|
+
(EnhancedInterval, "interval"),
|
|
292
|
+
# Binary abstract types
|
|
293
|
+
(EnhancedLargeBinary, "binary", ["bytes"]),
|
|
294
|
+
# UUID abstract types
|
|
295
|
+
(EnhancedUuid, "uuid"),
|
|
296
|
+
# Special types
|
|
297
|
+
(EnhancedJSON, "json", ["dict"]),
|
|
298
|
+
]
|
|
299
|
+
|
|
300
|
+
# Special types
|
|
301
|
+
special_types = [
|
|
302
|
+
(
|
|
303
|
+
{
|
|
304
|
+
"type": ARRAY,
|
|
305
|
+
"arguments": [
|
|
306
|
+
{
|
|
307
|
+
"name": "item_type",
|
|
308
|
+
"type": Any,
|
|
309
|
+
"required": True,
|
|
310
|
+
"default": None,
|
|
311
|
+
"transform": _transform_array_item_type,
|
|
312
|
+
"positional": True,
|
|
313
|
+
},
|
|
314
|
+
{"name": "dimensions", "type": int, "required": False, "default": 1},
|
|
315
|
+
],
|
|
316
|
+
},
|
|
317
|
+
"array",
|
|
318
|
+
None,
|
|
319
|
+
),
|
|
320
|
+
(
|
|
321
|
+
{
|
|
322
|
+
"type": Enum,
|
|
323
|
+
"arguments": [
|
|
324
|
+
{"name": "enum_class", "type": type[Any], "required": True, "default": None, "positional": True}
|
|
325
|
+
],
|
|
326
|
+
},
|
|
327
|
+
"enum",
|
|
328
|
+
None,
|
|
329
|
+
),
|
|
330
|
+
]
|
|
331
|
+
|
|
332
|
+
# Register all types
|
|
333
|
+
for type_info in builtin_types + special_types:
|
|
334
|
+
field_type = type_info[0]
|
|
335
|
+
type_name = type_info[1]
|
|
336
|
+
aliases = type_info[2] if len(type_info) > 2 else []
|
|
337
|
+
self.register(field_type, type_name, aliases=aliases)
|
|
338
|
+
|
|
339
|
+
self._initialized = True
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
def register_field_type(
|
|
343
|
+
type_def: TypeDefinition | type[Any], type_name: str, *, aliases: list[str] | None = None
|
|
344
|
+
) -> None:
|
|
345
|
+
"""Register a field type in the global registry
|
|
346
|
+
|
|
347
|
+
Args:
|
|
348
|
+
type_def: Type definition or type class to register
|
|
349
|
+
type_name: Name to register the type under
|
|
350
|
+
aliases: Optional list of alias names
|
|
351
|
+
"""
|
|
352
|
+
_registry.register(type_def, type_name, aliases=aliases)
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
def create_type_instance(type_name: str, kwargs: dict[str, Any]) -> Any:
|
|
356
|
+
"""Create type instance from name and parameters
|
|
357
|
+
|
|
358
|
+
Args:
|
|
359
|
+
type_name: Name of the type to create
|
|
360
|
+
kwargs: Parameters for type construction
|
|
361
|
+
|
|
362
|
+
Returns:
|
|
363
|
+
Instantiated SQLAlchemy type object
|
|
364
|
+
"""
|
|
365
|
+
type_def = _registry.get_type(type_name)
|
|
366
|
+
type_params = _get_type_params(type_def, kwargs)
|
|
367
|
+
return _registry.create_type_instance(type_name, type_params)
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
def get_type_definition(type_name: str) -> TypeDefinition:
|
|
371
|
+
"""Get type definition by name
|
|
372
|
+
|
|
373
|
+
Args:
|
|
374
|
+
type_name: Name of the type to look up
|
|
375
|
+
|
|
376
|
+
Returns:
|
|
377
|
+
Type definition for the requested type
|
|
378
|
+
"""
|
|
379
|
+
return _registry.get_type(type_name)
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
# Global registry instance
|
|
383
|
+
_registry = TypeRegistry()
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
# === Auto Type ===
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
class Auto(TypeEngine):
|
|
390
|
+
"""Automatic type inference placeholder, similar to SQLAlchemy's NullType
|
|
391
|
+
|
|
392
|
+
Used as a placeholder for automatic type inference based on default values
|
|
393
|
+
or other context clues.
|
|
394
|
+
"""
|
|
395
|
+
|
|
396
|
+
def __init__(self):
|
|
397
|
+
super().__init__()
|
|
398
|
+
|
|
399
|
+
def get_col_spec(self): # noqa
|
|
400
|
+
return "AUTO"
|
|
401
|
+
|
|
402
|
+
def bind_processor(self, dialect):
|
|
403
|
+
return None
|
|
404
|
+
|
|
405
|
+
def result_processor(self, dialect, coltype):
|
|
406
|
+
return None
|
|
407
|
+
|
|
408
|
+
def __repr__(self):
|
|
409
|
+
return "Auto()"
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
# === Enhanced Types ===
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
class EnhancedStringComparator(String.Comparator, StringFunctionMixin): # pyright: ignore[reportIncompatibleMethodOverride]
|
|
416
|
+
"""Enhanced string comparator with direct function call support
|
|
417
|
+
|
|
418
|
+
Provides string-specific comparison methods and function chaining.
|
|
419
|
+
"""
|
|
420
|
+
|
|
421
|
+
def matches(self, pattern: str) -> ColumnElement[bool]:
|
|
422
|
+
return self.expr.op("~")(pattern)
|
|
423
|
+
|
|
424
|
+
def length_between(self, min_len: int, max_len: int) -> ColumnElement[bool]:
|
|
425
|
+
return func.length(self.expr).between(min_len, max_len)
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+
class EnhancedIntegerComparator(Integer.Comparator, NumericFunctionMixin):
|
|
429
|
+
"""Enhanced integer comparator with numeric functions
|
|
430
|
+
|
|
431
|
+
Provides integer-specific comparison methods and numeric function chaining.
|
|
432
|
+
"""
|
|
433
|
+
|
|
434
|
+
pass
|
|
435
|
+
|
|
436
|
+
|
|
437
|
+
class EnhancedNumericComparator(Numeric.Comparator, NumericFunctionMixin):
|
|
438
|
+
"""Enhanced numeric comparator with mathematical functions
|
|
439
|
+
|
|
440
|
+
Provides numeric-specific comparison methods and mathematical function chaining.
|
|
441
|
+
"""
|
|
442
|
+
|
|
443
|
+
pass
|
|
444
|
+
|
|
445
|
+
|
|
446
|
+
class EnhancedDateTimeComparator(DateTime.Comparator, DateTimeFunctionMixin):
|
|
447
|
+
"""Enhanced datetime comparator with date/time functions
|
|
448
|
+
|
|
449
|
+
Provides datetime-specific comparison methods and date/time function chaining.
|
|
450
|
+
"""
|
|
451
|
+
|
|
452
|
+
def is_today(self) -> ColumnElement[bool]:
|
|
453
|
+
return func.date(self.expr) == func.current_date() # noqa
|
|
454
|
+
|
|
455
|
+
def is_past(self) -> ColumnElement[bool]:
|
|
456
|
+
return self.expr < func.now()
|
|
457
|
+
|
|
458
|
+
def is_future(self) -> ColumnElement[bool]:
|
|
459
|
+
return self.expr > func.now()
|
|
460
|
+
|
|
461
|
+
def year_equals(self, year: int) -> ColumnElement[bool]:
|
|
462
|
+
return func.extract("year", self.expr) == year # noqa
|
|
463
|
+
|
|
464
|
+
def month_equals(self, month: int) -> ColumnElement[bool]:
|
|
465
|
+
return func.extract("month", self.expr) == month # noqa
|
|
466
|
+
|
|
467
|
+
|
|
468
|
+
class EnhancedBooleanComparator(Boolean.Comparator, FunctionMixin):
|
|
469
|
+
"""Enhanced boolean comparator with general functions
|
|
470
|
+
|
|
471
|
+
Provides boolean-specific comparison methods and general function support.
|
|
472
|
+
"""
|
|
473
|
+
|
|
474
|
+
def _get_expression(self):
|
|
475
|
+
"""Get current expression - uses Comparator's expr attribute
|
|
476
|
+
|
|
477
|
+
Returns:
|
|
478
|
+
The expression object for function operations
|
|
479
|
+
"""
|
|
480
|
+
return self.expr
|
|
481
|
+
|
|
482
|
+
def is_true(self) -> ColumnElement[bool]:
|
|
483
|
+
return self.expr.is_(True)
|
|
484
|
+
|
|
485
|
+
def is_false(self) -> ColumnElement[bool]:
|
|
486
|
+
return self.expr.is_(False)
|
|
487
|
+
|
|
488
|
+
|
|
489
|
+
class EnhancedJSONComparator(JSON.Comparator):
|
|
490
|
+
"""Enhanced JSON comparator with JSON-specific operations
|
|
491
|
+
|
|
492
|
+
Provides JSON path operations and key existence checks.
|
|
493
|
+
"""
|
|
494
|
+
|
|
495
|
+
def has_key(self, key: str) -> ColumnElement[bool]:
|
|
496
|
+
return self.expr.op("?")(key)
|
|
497
|
+
|
|
498
|
+
def has_keys(self, *keys) -> ColumnElement[bool]:
|
|
499
|
+
return self.expr.op("?&")(list(keys))
|
|
500
|
+
|
|
501
|
+
def has_any_key(self, *keys) -> ColumnElement[bool]:
|
|
502
|
+
return self.expr.op("?|")(list(keys))
|
|
503
|
+
|
|
504
|
+
def path_exists(self, path: str) -> ColumnElement[bool]:
|
|
505
|
+
return func.json_extract_path(self.expr, path).is_not(None)
|
|
506
|
+
|
|
507
|
+
def extract_text(self, path: str) -> "FunctionExpression":
|
|
508
|
+
return FunctionExpression(func.json_extract_path_text(self.expr, path))
|
|
509
|
+
|
|
510
|
+
|
|
511
|
+
class EnhancedString(String):
|
|
512
|
+
"""Enhanced string type with function chaining support"""
|
|
513
|
+
|
|
514
|
+
comparator_factory = EnhancedStringComparator
|
|
515
|
+
|
|
516
|
+
|
|
517
|
+
class EnhancedText(Text):
|
|
518
|
+
"""Enhanced text type with function chaining support"""
|
|
519
|
+
|
|
520
|
+
comparator_factory = EnhancedStringComparator
|
|
521
|
+
|
|
522
|
+
|
|
523
|
+
class EnhancedInteger(Integer):
|
|
524
|
+
"""Enhanced integer type with numeric function chaining support"""
|
|
525
|
+
|
|
526
|
+
comparator_factory = EnhancedIntegerComparator
|
|
527
|
+
|
|
528
|
+
|
|
529
|
+
class EnhancedBigInteger(BigInteger):
|
|
530
|
+
"""Enhanced big integer type with numeric function chaining support"""
|
|
531
|
+
|
|
532
|
+
comparator_factory = EnhancedIntegerComparator
|
|
533
|
+
|
|
534
|
+
|
|
535
|
+
class EnhancedSmallInteger(SmallInteger):
|
|
536
|
+
"""Enhanced small integer type with numeric function chaining support"""
|
|
537
|
+
|
|
538
|
+
comparator_factory = EnhancedIntegerComparator
|
|
539
|
+
|
|
540
|
+
|
|
541
|
+
class EnhancedFloat(Float):
|
|
542
|
+
"""Enhanced float type with numeric function chaining support"""
|
|
543
|
+
|
|
544
|
+
comparator_factory = EnhancedNumericComparator
|
|
545
|
+
|
|
546
|
+
|
|
547
|
+
class EnhancedNumeric(Numeric):
|
|
548
|
+
"""Enhanced numeric type with mathematical function chaining support"""
|
|
549
|
+
|
|
550
|
+
comparator_factory = EnhancedNumericComparator
|
|
551
|
+
|
|
552
|
+
|
|
553
|
+
class EnhancedDateTime(DateTime):
|
|
554
|
+
"""Enhanced datetime type with date/time function chaining support"""
|
|
555
|
+
|
|
556
|
+
comparator_factory = EnhancedDateTimeComparator
|
|
557
|
+
|
|
558
|
+
|
|
559
|
+
class EnhancedDate(Date):
|
|
560
|
+
"""Enhanced date type with date function chaining support"""
|
|
561
|
+
|
|
562
|
+
comparator_factory = EnhancedDateTimeComparator
|
|
563
|
+
|
|
564
|
+
|
|
565
|
+
class EnhancedTime(Time):
|
|
566
|
+
"""Enhanced time type with time function chaining support"""
|
|
567
|
+
|
|
568
|
+
comparator_factory = EnhancedDateTimeComparator
|
|
569
|
+
|
|
570
|
+
|
|
571
|
+
class EnhancedBoolean(Boolean):
|
|
572
|
+
"""Enhanced boolean type with boolean-specific operations"""
|
|
573
|
+
|
|
574
|
+
comparator_factory = EnhancedBooleanComparator
|
|
575
|
+
|
|
576
|
+
|
|
577
|
+
class EnhancedJSON(JSON):
|
|
578
|
+
"""Enhanced JSON type with JSON path operations"""
|
|
579
|
+
|
|
580
|
+
comparator_factory = EnhancedJSONComparator
|
|
581
|
+
|
|
582
|
+
|
|
583
|
+
class EnhancedDouble(Double):
|
|
584
|
+
"""Enhanced double precision float type with numeric function chaining support"""
|
|
585
|
+
|
|
586
|
+
comparator_factory = EnhancedNumericComparator
|
|
587
|
+
|
|
588
|
+
|
|
589
|
+
class EnhancedUuid(Uuid):
|
|
590
|
+
"""Enhanced UUID type with string function chaining support"""
|
|
591
|
+
|
|
592
|
+
comparator_factory = EnhancedStringComparator
|
|
593
|
+
|
|
594
|
+
|
|
595
|
+
class EnhancedLargeBinary(LargeBinary):
|
|
596
|
+
"""Enhanced binary type for large binary data"""
|
|
597
|
+
|
|
598
|
+
comparator_factory = LargeBinary.Comparator
|
|
599
|
+
|
|
600
|
+
|
|
601
|
+
class EnhancedInterval(Interval):
|
|
602
|
+
"""Enhanced interval type for time intervals"""
|
|
603
|
+
|
|
604
|
+
comparator_factory = Interval.Comparator
|
|
605
|
+
|
|
606
|
+
|
|
607
|
+
# === Column Classes ===
|
|
608
|
+
|
|
609
|
+
|
|
610
|
+
EnhancedType = TypeVar("EnhancedType")
|
|
611
|
+
|
|
612
|
+
|
|
613
|
+
class ColumnAttribute(Generic[EnhancedType], FunctionMixin):
|
|
614
|
+
"""Enhanced chaining operations for Core Column
|
|
615
|
+
|
|
616
|
+
Provides function chaining, type-specific operations, and metadata access
|
|
617
|
+
for SQLAlchemy Core columns.
|
|
618
|
+
"""
|
|
619
|
+
|
|
620
|
+
def __init__(self, column: CoreColumn, model_class: type[Any]): # noqa
|
|
621
|
+
self.column = column # Public attribute: users can access underlying SQLAlchemy Column
|
|
622
|
+
self.model_class = model_class # Public attribute: for introspection and debugging
|
|
623
|
+
self._comparator = self._create_comparator()
|
|
624
|
+
|
|
625
|
+
self._enhanced_params = self._get_info_params("_enhanced")
|
|
626
|
+
self._performance_params = self._get_info_params("_performance")
|
|
627
|
+
self._codegen_params = self._get_info_params("_codegen")
|
|
628
|
+
|
|
629
|
+
def _get_expression(self):
|
|
630
|
+
"""Get current expression - returns column object
|
|
631
|
+
|
|
632
|
+
Returns:
|
|
633
|
+
The underlying SQLAlchemy column for function operations
|
|
634
|
+
"""
|
|
635
|
+
return self.column
|
|
636
|
+
|
|
637
|
+
def _create_comparator(self) -> Any:
|
|
638
|
+
"""Create appropriate comparator based on column type
|
|
639
|
+
|
|
640
|
+
Returns:
|
|
641
|
+
Type-specific comparator instance or None
|
|
642
|
+
"""
|
|
643
|
+
column_type = self.column.type
|
|
644
|
+
|
|
645
|
+
# Get comparator factory class
|
|
646
|
+
comparator_class = getattr(column_type, "comparator_factory", None)
|
|
647
|
+
if comparator_class:
|
|
648
|
+
# Create comparator instance, passing column as expr
|
|
649
|
+
return comparator_class(self.column)
|
|
650
|
+
|
|
651
|
+
# If no custom comparator, return None
|
|
652
|
+
return None
|
|
653
|
+
|
|
654
|
+
def __getattr__(self, name: str) -> Any:
|
|
655
|
+
"""Smart attribute lookup: comparator -> FunctionMixin -> column
|
|
656
|
+
|
|
657
|
+
Args:
|
|
658
|
+
name: Attribute name to look up
|
|
659
|
+
|
|
660
|
+
Returns:
|
|
661
|
+
Attribute value from the appropriate source
|
|
662
|
+
|
|
663
|
+
Raises:
|
|
664
|
+
AttributeError: If attribute is not found
|
|
665
|
+
"""
|
|
666
|
+
# 1. Try comparator first (type-specific methods)
|
|
667
|
+
if self._comparator and hasattr(self._comparator, name):
|
|
668
|
+
return getattr(self._comparator, name)
|
|
669
|
+
|
|
670
|
+
# 2. Try FunctionMixin (general methods) - handled automatically through MRO
|
|
671
|
+
try:
|
|
672
|
+
return super().__getattribute__(name)
|
|
673
|
+
except AttributeError:
|
|
674
|
+
pass
|
|
675
|
+
|
|
676
|
+
# 3. Try column native methods (except already exposed attributes)
|
|
677
|
+
if name not in ("column", "model_class", "name", "type") and hasattr(self.column, name):
|
|
678
|
+
return getattr(self.column, name)
|
|
679
|
+
|
|
680
|
+
raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{name}'")
|
|
681
|
+
|
|
682
|
+
# Proxy common query operations
|
|
683
|
+
def __eq__(self, other) -> ColumnElement[bool]: # type: ignore[reportIncompatibleMethodOverride]
|
|
684
|
+
return self.column == other
|
|
685
|
+
|
|
686
|
+
def __ne__(self, other) -> ColumnElement[bool]: # type: ignore[reportIncompatibleMethodOverride]
|
|
687
|
+
return self.column != other
|
|
688
|
+
|
|
689
|
+
def __lt__(self, other) -> ColumnElement[bool]:
|
|
690
|
+
return self.column < other
|
|
691
|
+
|
|
692
|
+
def __le__(self, other) -> ColumnElement[bool]:
|
|
693
|
+
return self.column <= other
|
|
694
|
+
|
|
695
|
+
def __gt__(self, other) -> ColumnElement[bool]:
|
|
696
|
+
return self.column > other
|
|
697
|
+
|
|
698
|
+
def __ge__(self, other) -> ColumnElement[bool]:
|
|
699
|
+
return self.column >= other
|
|
700
|
+
|
|
701
|
+
@property
|
|
702
|
+
def name(self) -> str | None:
|
|
703
|
+
"""Get field name
|
|
704
|
+
|
|
705
|
+
Returns:
|
|
706
|
+
Field name or None if not set
|
|
707
|
+
"""
|
|
708
|
+
return self.column.name
|
|
709
|
+
|
|
710
|
+
@property
|
|
711
|
+
def type(self):
|
|
712
|
+
"""Get field type
|
|
713
|
+
|
|
714
|
+
Returns:
|
|
715
|
+
SQLAlchemy type instance
|
|
716
|
+
"""
|
|
717
|
+
return self.column.type
|
|
718
|
+
|
|
719
|
+
def _get_info_params(self, key: str) -> dict[str, Any]:
|
|
720
|
+
"""Get parameters from column info
|
|
721
|
+
|
|
722
|
+
Args:
|
|
723
|
+
key: Info key to retrieve
|
|
724
|
+
|
|
725
|
+
Returns:
|
|
726
|
+
Parameter dictionary or empty dict
|
|
727
|
+
"""
|
|
728
|
+
return self.column.info.get(key, {}) if self.column.info else {}
|
|
729
|
+
|
|
730
|
+
# Essential functionality parameter access
|
|
731
|
+
@property
|
|
732
|
+
def validators(self) -> list[Any]:
|
|
733
|
+
"""Field validators
|
|
734
|
+
|
|
735
|
+
Returns:
|
|
736
|
+
List of validator functions
|
|
737
|
+
"""
|
|
738
|
+
return self._enhanced_params.get("validators", [])
|
|
739
|
+
|
|
740
|
+
def get_default_factory(self) -> Callable[[], Any] | None:
|
|
741
|
+
"""Get default value factory function
|
|
742
|
+
|
|
743
|
+
Returns:
|
|
744
|
+
Default factory function or None
|
|
745
|
+
"""
|
|
746
|
+
return self._enhanced_params.get("default_factory")
|
|
747
|
+
|
|
748
|
+
def get_insert_default(self) -> Any:
|
|
749
|
+
"""Get insert-time default value
|
|
750
|
+
|
|
751
|
+
Returns:
|
|
752
|
+
Insert default value or None
|
|
753
|
+
"""
|
|
754
|
+
return self._enhanced_params.get("insert_default")
|
|
755
|
+
|
|
756
|
+
def has_insert_default(self) -> bool:
|
|
757
|
+
"""Check if field has insert default value
|
|
758
|
+
|
|
759
|
+
Returns:
|
|
760
|
+
True if insert default is set
|
|
761
|
+
"""
|
|
762
|
+
return "insert_default" in self._enhanced_params
|
|
763
|
+
|
|
764
|
+
def get_effective_default(self) -> Any:
|
|
765
|
+
"""Get effective default value (by priority)
|
|
766
|
+
|
|
767
|
+
Priority: default > default_factory > insert_default
|
|
768
|
+
|
|
769
|
+
Returns:
|
|
770
|
+
Effective default value or None
|
|
771
|
+
"""
|
|
772
|
+
# Priority: default > default_factory > insert_default
|
|
773
|
+
if self.column.default is not None:
|
|
774
|
+
return self.column.default
|
|
775
|
+
|
|
776
|
+
default_factory = self.get_default_factory()
|
|
777
|
+
if default_factory is not None:
|
|
778
|
+
return default_factory
|
|
779
|
+
|
|
780
|
+
insert_default = self.get_insert_default()
|
|
781
|
+
if insert_default is not None:
|
|
782
|
+
return insert_default
|
|
783
|
+
|
|
784
|
+
return None
|
|
785
|
+
|
|
786
|
+
def validate_value(self, value: Any, field_name: str) -> Any:
|
|
787
|
+
"""Validate field value using registered validators
|
|
788
|
+
|
|
789
|
+
Args:
|
|
790
|
+
value: Value to validate
|
|
791
|
+
field_name: Name of the field being validated
|
|
792
|
+
|
|
793
|
+
Returns:
|
|
794
|
+
Validated value
|
|
795
|
+
"""
|
|
796
|
+
validators = self.validators
|
|
797
|
+
if validators:
|
|
798
|
+
from .validators import validate_field_value
|
|
799
|
+
|
|
800
|
+
return validate_field_value(validators, value, field_name)
|
|
801
|
+
return value
|
|
802
|
+
|
|
803
|
+
@property
|
|
804
|
+
def is_deferred(self) -> bool:
|
|
805
|
+
"""Whether field is deferred loading
|
|
806
|
+
|
|
807
|
+
Returns:
|
|
808
|
+
True if field is deferred
|
|
809
|
+
"""
|
|
810
|
+
return self._performance_params.get("deferred", False)
|
|
811
|
+
|
|
812
|
+
# Experience enhancement parameter access
|
|
813
|
+
@property
|
|
814
|
+
def deferred_group(self) -> str | None:
|
|
815
|
+
"""Deferred loading group name
|
|
816
|
+
|
|
817
|
+
Returns:
|
|
818
|
+
Deferred group name or None
|
|
819
|
+
"""
|
|
820
|
+
return self._performance_params.get("deferred_group")
|
|
821
|
+
|
|
822
|
+
@property
|
|
823
|
+
def include_in_init(self) -> bool | None:
|
|
824
|
+
"""Whether to include in __init__ method
|
|
825
|
+
|
|
826
|
+
Returns:
|
|
827
|
+
True/False or None for default behavior
|
|
828
|
+
"""
|
|
829
|
+
return self._codegen_params.get("init")
|
|
830
|
+
|
|
831
|
+
@property
|
|
832
|
+
def include_in_repr(self) -> bool | None:
|
|
833
|
+
"""Whether to include in __repr__ method
|
|
834
|
+
|
|
835
|
+
Returns:
|
|
836
|
+
True/False or None for default behavior
|
|
837
|
+
"""
|
|
838
|
+
return self._codegen_params.get("repr")
|
|
839
|
+
|
|
840
|
+
@property
|
|
841
|
+
def include_in_compare(self) -> bool | None:
|
|
842
|
+
"""Whether to use for comparison operations
|
|
843
|
+
|
|
844
|
+
Returns:
|
|
845
|
+
True/False or None for default behavior
|
|
846
|
+
"""
|
|
847
|
+
return self._codegen_params.get("compare")
|
|
848
|
+
|
|
849
|
+
@property
|
|
850
|
+
def has_active_history(self) -> bool:
|
|
851
|
+
"""Whether active history tracking is enabled
|
|
852
|
+
|
|
853
|
+
Returns:
|
|
854
|
+
True if active history is enabled
|
|
855
|
+
"""
|
|
856
|
+
return self._performance_params.get("active_history", False)
|
|
857
|
+
|
|
858
|
+
@property
|
|
859
|
+
def deferred_raiseload(self) -> bool | None:
|
|
860
|
+
"""Deferred loading exception handling
|
|
861
|
+
|
|
862
|
+
Returns:
|
|
863
|
+
True/False or None for default behavior
|
|
864
|
+
"""
|
|
865
|
+
return self._performance_params.get("deferred_raiseload")
|
|
866
|
+
|
|
867
|
+
@property
|
|
868
|
+
def include_in_hash(self) -> bool | None:
|
|
869
|
+
"""Whether to use for hash calculation
|
|
870
|
+
|
|
871
|
+
Returns:
|
|
872
|
+
True/False or None for default behavior
|
|
873
|
+
"""
|
|
874
|
+
return self._codegen_params.get("hash")
|
|
875
|
+
|
|
876
|
+
@property
|
|
877
|
+
def is_kw_only(self) -> bool | None:
|
|
878
|
+
"""Whether field is keyword-only parameter
|
|
879
|
+
|
|
880
|
+
Returns:
|
|
881
|
+
True/False or None for default behavior
|
|
882
|
+
"""
|
|
883
|
+
return self._codegen_params.get("kw_only")
|
|
884
|
+
|
|
885
|
+
# General parameter access methods
|
|
886
|
+
def get_param(self, category: str, name: str, default: Any = None) -> Any:
|
|
887
|
+
"""Get parameter from specified category
|
|
888
|
+
|
|
889
|
+
Args:
|
|
890
|
+
category: Parameter category (enhanced, performance, codegen)
|
|
891
|
+
name: Parameter name
|
|
892
|
+
default: Default value if not found
|
|
893
|
+
|
|
894
|
+
Returns:
|
|
895
|
+
Parameter value or default
|
|
896
|
+
"""
|
|
897
|
+
param_dict = getattr(self, f"_{category}_params", {})
|
|
898
|
+
return param_dict.get(name, default)
|
|
899
|
+
|
|
900
|
+
def get_codegen_params(self) -> dict[str, Any]:
|
|
901
|
+
"""Get code generation parameters
|
|
902
|
+
|
|
903
|
+
Returns:
|
|
904
|
+
Dictionary of code generation parameters
|
|
905
|
+
"""
|
|
906
|
+
return self._codegen_params
|
|
907
|
+
|
|
908
|
+
def get_field_metadata(self) -> dict[str, Any]:
|
|
909
|
+
"""Get complete field metadata information
|
|
910
|
+
|
|
911
|
+
Returns:
|
|
912
|
+
Dictionary containing all field metadata
|
|
913
|
+
"""
|
|
914
|
+
metadata = {
|
|
915
|
+
"name": self.name,
|
|
916
|
+
"type": str(self.type),
|
|
917
|
+
"nullable": getattr(self.column, "nullable", True),
|
|
918
|
+
"primary_key": getattr(self.column, "primary_key", False),
|
|
919
|
+
"unique": getattr(self.column, "unique", False),
|
|
920
|
+
"index": getattr(self.column, "index", False),
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
# Add comments and documentation
|
|
924
|
+
if hasattr(self.column, "comment") and self.column.comment:
|
|
925
|
+
metadata["comment"] = self.column.comment
|
|
926
|
+
if hasattr(self.column, "doc") and self.column.doc:
|
|
927
|
+
metadata["doc"] = self.column.doc
|
|
928
|
+
|
|
929
|
+
# Add extended parameters
|
|
930
|
+
if self._enhanced_params:
|
|
931
|
+
metadata["enhanced"] = self._enhanced_params
|
|
932
|
+
if self._performance_params:
|
|
933
|
+
metadata["performance"] = self._performance_params
|
|
934
|
+
if self._codegen_params:
|
|
935
|
+
metadata["codegen"] = self._codegen_params
|
|
936
|
+
|
|
937
|
+
return metadata
|
|
938
|
+
|
|
939
|
+
def __set_name__(self, owner: builtins.type[Any], name: str) -> None:
|
|
940
|
+
"""Proxy to column's __set_name__ method if it exists
|
|
941
|
+
|
|
942
|
+
Args:
|
|
943
|
+
owner: Owner class
|
|
944
|
+
name: Attribute name
|
|
945
|
+
"""
|
|
946
|
+
set_name_attr = getattr(self.column, "__set_name__", None)
|
|
947
|
+
if set_name_attr is not None and callable(set_name_attr):
|
|
948
|
+
set_name_attr(owner, name)
|
|
949
|
+
|
|
950
|
+
|
|
951
|
+
class Column(Generic[T]):
|
|
952
|
+
"""Field descriptor supporting type annotations and field access
|
|
953
|
+
|
|
954
|
+
Provides a descriptor interface for SQLAlchemy columns with type safety
|
|
955
|
+
and enhanced functionality.
|
|
956
|
+
"""
|
|
957
|
+
|
|
958
|
+
def __init__(self, column: CoreColumn): # noqa
|
|
959
|
+
self.column = column
|
|
960
|
+
self.name: str | None = None
|
|
961
|
+
self._private_name: str | None = None
|
|
962
|
+
|
|
963
|
+
def __set_name__(self, owner: type[Any], name: str) -> None:
|
|
964
|
+
"""Called when descriptor is assigned to class attribute
|
|
965
|
+
|
|
966
|
+
Args:
|
|
967
|
+
owner: Owner class
|
|
968
|
+
name: Attribute name
|
|
969
|
+
"""
|
|
970
|
+
self.name = name
|
|
971
|
+
self._private_name = f"_{name}"
|
|
972
|
+
|
|
973
|
+
# If column has no name, use field name
|
|
974
|
+
if self.column.name is None:
|
|
975
|
+
self.column.name = name
|
|
976
|
+
|
|
977
|
+
@overload
|
|
978
|
+
def __get__(self, instance: None, owner: type[Any]) -> "ColumnAttribute[TypeEngine[T]]": ...
|
|
979
|
+
|
|
980
|
+
@overload
|
|
981
|
+
def __get__(self, instance: Any, owner: type[Any]) -> T: ...
|
|
982
|
+
|
|
983
|
+
def __get__(self, instance: Any, owner: type[Any]) -> Any:
|
|
984
|
+
"""Get field value
|
|
985
|
+
|
|
986
|
+
Args:
|
|
987
|
+
instance: Model instance or None for class access
|
|
988
|
+
owner: Owner class
|
|
989
|
+
|
|
990
|
+
Returns:
|
|
991
|
+
ColumnAttribute for class access, field value for instance access
|
|
992
|
+
"""
|
|
993
|
+
if instance is None:
|
|
994
|
+
# Class access: return enhanced Column proxy for queries
|
|
995
|
+
return ColumnAttribute(self.column, owner)
|
|
996
|
+
else:
|
|
997
|
+
# Instance access: return actual stored value
|
|
998
|
+
if self._private_name is None:
|
|
999
|
+
return None
|
|
1000
|
+
return getattr(instance, self._private_name, None)
|
|
1001
|
+
|
|
1002
|
+
def __set__(self, instance: Any, value: T) -> None:
|
|
1003
|
+
"""Set field value
|
|
1004
|
+
|
|
1005
|
+
Args:
|
|
1006
|
+
instance: Model instance
|
|
1007
|
+
value: Value to set
|
|
1008
|
+
"""
|
|
1009
|
+
if instance is None:
|
|
1010
|
+
raise AttributeError("Cannot set attribute on class")
|
|
1011
|
+
|
|
1012
|
+
# Instance assignment: store to private attribute
|
|
1013
|
+
if self._private_name is not None:
|
|
1014
|
+
setattr(instance, self._private_name, value)
|
|
1015
|
+
|
|
1016
|
+
def __delete__(self, instance: Any) -> None:
|
|
1017
|
+
"""Delete field value
|
|
1018
|
+
|
|
1019
|
+
Args:
|
|
1020
|
+
instance: Model instance
|
|
1021
|
+
"""
|
|
1022
|
+
if instance is None:
|
|
1023
|
+
raise AttributeError("Cannot delete attribute on class")
|
|
1024
|
+
|
|
1025
|
+
if self._private_name is not None and hasattr(instance, self._private_name):
|
|
1026
|
+
delattr(instance, self._private_name)
|
|
1027
|
+
|
|
1028
|
+
|
|
1029
|
+
def _apply_codegen_defaults(codegen_params: dict, column_kwargs: dict) -> dict:
|
|
1030
|
+
"""应用代码生成参数的智能默认值"""
|
|
1031
|
+
# 定义基于 ORM 字段特性的智能默认值
|
|
1032
|
+
defaults = {"init": True, "repr": True, "compare": False, "hash": None, "kw_only": False}
|
|
1033
|
+
|
|
1034
|
+
# 主键字段:不参与初始化,但参与比较和显示
|
|
1035
|
+
if column_kwargs.get("primary_key"):
|
|
1036
|
+
defaults.update({"init": False, "repr": True, "compare": True})
|
|
1037
|
+
|
|
1038
|
+
# 自增字段:只有当它是 True 时才不参与初始化("auto" 不算)
|
|
1039
|
+
if column_kwargs.get("autoincrement") is True: # noqa
|
|
1040
|
+
defaults["init"] = False
|
|
1041
|
+
|
|
1042
|
+
# 服务器默认值字段:不参与初始化
|
|
1043
|
+
if column_kwargs.get("server_default") is not None:
|
|
1044
|
+
defaults["init"] = False
|
|
1045
|
+
|
|
1046
|
+
# 只为未显式设置的参数应用默认值
|
|
1047
|
+
for key, default_value in defaults.items():
|
|
1048
|
+
if key not in codegen_params:
|
|
1049
|
+
codegen_params[key] = default_value
|
|
1050
|
+
|
|
1051
|
+
return codegen_params
|
|
1052
|
+
|
|
1053
|
+
|
|
1054
|
+
def column(
|
|
1055
|
+
*,
|
|
1056
|
+
type: str = "auto", # noqa
|
|
1057
|
+
name: str | None = None,
|
|
1058
|
+
# SQLAlchemy Column parameters
|
|
1059
|
+
primary_key: bool = False,
|
|
1060
|
+
nullable: bool = True,
|
|
1061
|
+
default: Any = None,
|
|
1062
|
+
index: bool = False,
|
|
1063
|
+
unique: bool = False,
|
|
1064
|
+
autoincrement: str | bool = "auto",
|
|
1065
|
+
doc: str | None = None,
|
|
1066
|
+
key: str | None = None,
|
|
1067
|
+
onupdate: Any = None,
|
|
1068
|
+
comment: str | None = None,
|
|
1069
|
+
system: bool = False,
|
|
1070
|
+
server_default: Any = None,
|
|
1071
|
+
server_onupdate: Any = None,
|
|
1072
|
+
quote: bool | None = None,
|
|
1073
|
+
info: dict[str, Any] | None = None,
|
|
1074
|
+
# Essential functionality parameters
|
|
1075
|
+
default_factory: Callable[[], Any] | None = None,
|
|
1076
|
+
validators: list[Any] | None = None,
|
|
1077
|
+
deferred: bool = False,
|
|
1078
|
+
# Experience enhancement parameters
|
|
1079
|
+
deferred_group: str | None = None,
|
|
1080
|
+
insert_default: Any = None,
|
|
1081
|
+
init: bool | None = None,
|
|
1082
|
+
repr: bool | None = None, # noqa
|
|
1083
|
+
compare: bool | None = None,
|
|
1084
|
+
# Advanced functionality parameters
|
|
1085
|
+
active_history: bool = False,
|
|
1086
|
+
deferred_raiseload: bool | None = None,
|
|
1087
|
+
hash: bool | None = None, # noqa
|
|
1088
|
+
kw_only: bool | None = None,
|
|
1089
|
+
# Type parameters (passed through **kwargs)
|
|
1090
|
+
**kwargs: Any,
|
|
1091
|
+
) -> "Column[Any]":
|
|
1092
|
+
"""Create field descriptor with automatic type inference by default
|
|
1093
|
+
|
|
1094
|
+
Args:
|
|
1095
|
+
type: Field type, defaults to "auto" for automatic inference
|
|
1096
|
+
name: Field name
|
|
1097
|
+
primary_key: Whether field is primary key
|
|
1098
|
+
nullable: Whether field allows NULL values
|
|
1099
|
+
default: Default value
|
|
1100
|
+
index: Whether to create index
|
|
1101
|
+
unique: Whether field is unique
|
|
1102
|
+
autoincrement: Auto-increment setting
|
|
1103
|
+
doc: Documentation string
|
|
1104
|
+
key: Field key name
|
|
1105
|
+
onupdate: Default value on update
|
|
1106
|
+
comment: Field comment
|
|
1107
|
+
system: Whether field is system field
|
|
1108
|
+
server_default: Server-side default value
|
|
1109
|
+
server_onupdate: Server-side update default
|
|
1110
|
+
quote: Whether to quote field name
|
|
1111
|
+
info: Additional info dictionary
|
|
1112
|
+
default_factory: Default value factory function
|
|
1113
|
+
validators: Field validator list
|
|
1114
|
+
deferred: Whether to defer loading
|
|
1115
|
+
deferred_group: Deferred loading group
|
|
1116
|
+
insert_default: Insert-time default value
|
|
1117
|
+
init: Whether to include in __init__
|
|
1118
|
+
repr: Whether to include in __repr__
|
|
1119
|
+
compare: Whether to use for comparison
|
|
1120
|
+
active_history: Whether to enable history tracking
|
|
1121
|
+
deferred_raiseload: Deferred loading exception handling
|
|
1122
|
+
hash: Whether to use for hash calculation
|
|
1123
|
+
kw_only: Whether keyword-only parameter
|
|
1124
|
+
**kwargs: Type-specific parameters (like length, precision, etc.)
|
|
1125
|
+
"""
|
|
1126
|
+
# Prepare info dictionary to store extended parameters
|
|
1127
|
+
column_info = info.copy() if info else {}
|
|
1128
|
+
|
|
1129
|
+
# Collect codegen parameters
|
|
1130
|
+
codegen_params = {}
|
|
1131
|
+
if init is not None:
|
|
1132
|
+
codegen_params["init"] = init
|
|
1133
|
+
if repr is not None:
|
|
1134
|
+
codegen_params["repr"] = repr
|
|
1135
|
+
if compare is not None:
|
|
1136
|
+
codegen_params["compare"] = compare
|
|
1137
|
+
if hash is not None:
|
|
1138
|
+
codegen_params["hash"] = hash
|
|
1139
|
+
if kw_only is not None:
|
|
1140
|
+
codegen_params["kw_only"] = kw_only
|
|
1141
|
+
|
|
1142
|
+
# Build column kwargs for intelligent defaults
|
|
1143
|
+
column_kwargs = {
|
|
1144
|
+
"primary_key": primary_key,
|
|
1145
|
+
"autoincrement": autoincrement,
|
|
1146
|
+
"server_default": server_default,
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
# Apply intelligent defaults for codegen parameters
|
|
1150
|
+
codegen_params = _apply_codegen_defaults(codegen_params, column_kwargs)
|
|
1151
|
+
|
|
1152
|
+
# Store parameters by category
|
|
1153
|
+
_store_performance_params(column_info, deferred, deferred_group, deferred_raiseload, active_history)
|
|
1154
|
+
_store_codegen_params(
|
|
1155
|
+
column_info,
|
|
1156
|
+
codegen_params.get("init"),
|
|
1157
|
+
codegen_params.get("repr"),
|
|
1158
|
+
codegen_params.get("compare"),
|
|
1159
|
+
codegen_params.get("hash"),
|
|
1160
|
+
codegen_params.get("kw_only"),
|
|
1161
|
+
)
|
|
1162
|
+
_store_enhanced_params(column_info, default_factory, insert_default, validators)
|
|
1163
|
+
|
|
1164
|
+
# Handle default value logic
|
|
1165
|
+
final_default = _resolve_default_value(default, default_factory, insert_default)
|
|
1166
|
+
|
|
1167
|
+
# Get type definition and parameters
|
|
1168
|
+
type_def = _registry.get_type(type)
|
|
1169
|
+
type_params = _extract_type_params(type_def, kwargs)
|
|
1170
|
+
|
|
1171
|
+
# Create type instance
|
|
1172
|
+
type_instance = _registry.create_type_instance(type, type_params)
|
|
1173
|
+
|
|
1174
|
+
# Build Column parameters
|
|
1175
|
+
column_params = {
|
|
1176
|
+
"primary_key": primary_key,
|
|
1177
|
+
"nullable": nullable,
|
|
1178
|
+
"default": final_default,
|
|
1179
|
+
"index": index,
|
|
1180
|
+
"unique": unique,
|
|
1181
|
+
"autoincrement": autoincrement,
|
|
1182
|
+
"doc": doc,
|
|
1183
|
+
"key": key,
|
|
1184
|
+
"onupdate": onupdate,
|
|
1185
|
+
"comment": comment,
|
|
1186
|
+
"system": system,
|
|
1187
|
+
"server_default": server_default,
|
|
1188
|
+
"server_onupdate": server_onupdate,
|
|
1189
|
+
"quote": quote,
|
|
1190
|
+
"info": column_info,
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
# Handle quote parameter - only pass if name is provided
|
|
1194
|
+
if quote is not None and name is None:
|
|
1195
|
+
# Remove quote parameter if no name is provided (SQLAlchemy requirement)
|
|
1196
|
+
column_params.pop("quote", None)
|
|
1197
|
+
|
|
1198
|
+
# Remove None value parameters (except nullable and info)
|
|
1199
|
+
column_params = {k: v for k, v in column_params.items() if v is not None or k in ("nullable", "info")}
|
|
1200
|
+
|
|
1201
|
+
# Create Core Column
|
|
1202
|
+
if name is not None:
|
|
1203
|
+
core_column = CoreColumn(name, type_instance, **column_params)
|
|
1204
|
+
else:
|
|
1205
|
+
core_column = CoreColumn(type_instance, **column_params)
|
|
1206
|
+
|
|
1207
|
+
return Column(core_column)
|
|
1208
|
+
|
|
1209
|
+
|
|
1210
|
+
# === Model Integration Utilities ===
|
|
1211
|
+
|
|
1212
|
+
|
|
1213
|
+
def str_column(
|
|
1214
|
+
*,
|
|
1215
|
+
length: int | None = None,
|
|
1216
|
+
default_factory: Callable[[], Any] | None = None,
|
|
1217
|
+
validators: list[Any] | None = None,
|
|
1218
|
+
deferred: bool = False,
|
|
1219
|
+
deferred_group: str | None = None,
|
|
1220
|
+
insert_default: Any = None,
|
|
1221
|
+
init: bool | None = None,
|
|
1222
|
+
repr: bool | None = None, # noqa
|
|
1223
|
+
compare: bool | None = None,
|
|
1224
|
+
primary_key: bool = False,
|
|
1225
|
+
nullable: bool = True,
|
|
1226
|
+
default: Any = None,
|
|
1227
|
+
index: bool = False,
|
|
1228
|
+
unique: bool = False,
|
|
1229
|
+
**kwargs: Any,
|
|
1230
|
+
) -> "Column[Any]":
|
|
1231
|
+
"""String field shortcut function
|
|
1232
|
+
|
|
1233
|
+
Creates a string column with enhanced functionality and type safety.
|
|
1234
|
+
|
|
1235
|
+
Args:
|
|
1236
|
+
length: Maximum string length
|
|
1237
|
+
default_factory: Default value factory function
|
|
1238
|
+
validators: Field validator list
|
|
1239
|
+
deferred: Whether to defer loading
|
|
1240
|
+
deferred_group: Deferred loading group
|
|
1241
|
+
insert_default: Insert-time default value
|
|
1242
|
+
init: Whether to include in __init__
|
|
1243
|
+
repr: Whether to include in __repr__
|
|
1244
|
+
compare: Whether to use for comparison
|
|
1245
|
+
primary_key: Whether field is primary key
|
|
1246
|
+
nullable: Whether field allows NULL values
|
|
1247
|
+
default: Default value
|
|
1248
|
+
index: Whether to create index
|
|
1249
|
+
unique: Whether field is unique
|
|
1250
|
+
**kwargs: Additional type-specific parameters
|
|
1251
|
+
|
|
1252
|
+
Returns:
|
|
1253
|
+
Column descriptor for string field
|
|
1254
|
+
"""
|
|
1255
|
+
return column(
|
|
1256
|
+
type="string",
|
|
1257
|
+
length=length,
|
|
1258
|
+
default_factory=default_factory,
|
|
1259
|
+
validators=validators,
|
|
1260
|
+
deferred=deferred,
|
|
1261
|
+
deferred_group=deferred_group,
|
|
1262
|
+
insert_default=insert_default,
|
|
1263
|
+
init=init,
|
|
1264
|
+
repr=repr,
|
|
1265
|
+
compare=compare,
|
|
1266
|
+
primary_key=primary_key,
|
|
1267
|
+
nullable=nullable,
|
|
1268
|
+
default=default,
|
|
1269
|
+
index=index,
|
|
1270
|
+
unique=unique,
|
|
1271
|
+
**kwargs,
|
|
1272
|
+
)
|
|
1273
|
+
|
|
1274
|
+
|
|
1275
|
+
def int_column(
|
|
1276
|
+
*,
|
|
1277
|
+
default_factory: Callable[[], Any] | None = None,
|
|
1278
|
+
validators: list[Any] | None = None,
|
|
1279
|
+
deferred: bool = False,
|
|
1280
|
+
primary_key: bool = False,
|
|
1281
|
+
nullable: bool = True,
|
|
1282
|
+
default: Any = None,
|
|
1283
|
+
index: bool = False,
|
|
1284
|
+
unique: bool = False,
|
|
1285
|
+
**kwargs: Any,
|
|
1286
|
+
) -> "Column[Any]":
|
|
1287
|
+
"""Integer field shortcut function
|
|
1288
|
+
|
|
1289
|
+
Creates an integer column with enhanced functionality and type safety.
|
|
1290
|
+
|
|
1291
|
+
Args:
|
|
1292
|
+
default_factory: Default value factory function
|
|
1293
|
+
validators: Field validator list
|
|
1294
|
+
deferred: Whether to defer loading
|
|
1295
|
+
primary_key: Whether field is primary key
|
|
1296
|
+
nullable: Whether field allows NULL values
|
|
1297
|
+
default: Default value
|
|
1298
|
+
index: Whether to create index
|
|
1299
|
+
unique: Whether field is unique
|
|
1300
|
+
**kwargs: Additional type-specific parameters
|
|
1301
|
+
|
|
1302
|
+
Returns:
|
|
1303
|
+
Column descriptor for integer field
|
|
1304
|
+
"""
|
|
1305
|
+
return column(
|
|
1306
|
+
type="integer",
|
|
1307
|
+
default_factory=default_factory,
|
|
1308
|
+
validators=validators,
|
|
1309
|
+
deferred=deferred,
|
|
1310
|
+
primary_key=primary_key,
|
|
1311
|
+
nullable=nullable,
|
|
1312
|
+
default=default,
|
|
1313
|
+
index=index,
|
|
1314
|
+
unique=unique,
|
|
1315
|
+
**kwargs,
|
|
1316
|
+
)
|
|
1317
|
+
|
|
1318
|
+
|
|
1319
|
+
def bool_column(
|
|
1320
|
+
*,
|
|
1321
|
+
default_factory: Callable[[], Any] | None = None,
|
|
1322
|
+
validators: list[Any] | None = None,
|
|
1323
|
+
deferred: bool = False,
|
|
1324
|
+
primary_key: bool = False,
|
|
1325
|
+
nullable: bool = True,
|
|
1326
|
+
default: Any = None,
|
|
1327
|
+
index: bool = False,
|
|
1328
|
+
unique: bool = False,
|
|
1329
|
+
**kwargs: Any,
|
|
1330
|
+
) -> "Column[Any]":
|
|
1331
|
+
"""Boolean field shortcut function
|
|
1332
|
+
|
|
1333
|
+
Creates a boolean column with enhanced functionality and type safety.
|
|
1334
|
+
|
|
1335
|
+
Args:
|
|
1336
|
+
default_factory: Default value factory function
|
|
1337
|
+
validators: Field validator list
|
|
1338
|
+
deferred: Whether to defer loading
|
|
1339
|
+
primary_key: Whether field is primary key
|
|
1340
|
+
nullable: Whether field allows NULL values
|
|
1341
|
+
default: Default value
|
|
1342
|
+
index: Whether to create index
|
|
1343
|
+
unique: Whether field is unique
|
|
1344
|
+
**kwargs: Additional type-specific parameters
|
|
1345
|
+
|
|
1346
|
+
Returns:
|
|
1347
|
+
Column descriptor for boolean field
|
|
1348
|
+
"""
|
|
1349
|
+
return column(
|
|
1350
|
+
type="boolean",
|
|
1351
|
+
default_factory=default_factory,
|
|
1352
|
+
validators=validators,
|
|
1353
|
+
deferred=deferred,
|
|
1354
|
+
primary_key=primary_key,
|
|
1355
|
+
nullable=nullable,
|
|
1356
|
+
default=default,
|
|
1357
|
+
index=index,
|
|
1358
|
+
unique=unique,
|
|
1359
|
+
**kwargs,
|
|
1360
|
+
)
|
|
1361
|
+
|
|
1362
|
+
|
|
1363
|
+
def json_column(
|
|
1364
|
+
*,
|
|
1365
|
+
default_factory: Callable[[], Any] | None = None,
|
|
1366
|
+
validators: list[Any] | None = None,
|
|
1367
|
+
deferred: bool = False,
|
|
1368
|
+
primary_key: bool = False,
|
|
1369
|
+
nullable: bool = True,
|
|
1370
|
+
default: Any = None,
|
|
1371
|
+
index: bool = False,
|
|
1372
|
+
unique: bool = False,
|
|
1373
|
+
**kwargs: Any,
|
|
1374
|
+
) -> "Column[Any]":
|
|
1375
|
+
"""JSON field shortcut function
|
|
1376
|
+
|
|
1377
|
+
Creates a JSON column with enhanced functionality and type safety.
|
|
1378
|
+
|
|
1379
|
+
Args:
|
|
1380
|
+
default_factory: Default value factory function
|
|
1381
|
+
validators: Field validator list
|
|
1382
|
+
deferred: Whether to defer loading
|
|
1383
|
+
primary_key: Whether field is primary key
|
|
1384
|
+
nullable: Whether field allows NULL values
|
|
1385
|
+
default: Default value
|
|
1386
|
+
index: Whether to create index
|
|
1387
|
+
unique: Whether field is unique
|
|
1388
|
+
**kwargs: Additional type-specific parameters
|
|
1389
|
+
|
|
1390
|
+
Returns:
|
|
1391
|
+
Column descriptor for JSON field
|
|
1392
|
+
"""
|
|
1393
|
+
return column(
|
|
1394
|
+
type="json",
|
|
1395
|
+
default_factory=default_factory,
|
|
1396
|
+
validators=validators,
|
|
1397
|
+
deferred=deferred,
|
|
1398
|
+
primary_key=primary_key,
|
|
1399
|
+
nullable=nullable,
|
|
1400
|
+
default=default,
|
|
1401
|
+
index=index,
|
|
1402
|
+
unique=unique,
|
|
1403
|
+
**kwargs,
|
|
1404
|
+
)
|
|
1405
|
+
|
|
1406
|
+
|
|
1407
|
+
def datetime_column(
|
|
1408
|
+
*,
|
|
1409
|
+
default_factory: Callable[[], Any] | None = None,
|
|
1410
|
+
validators: list[Any] | None = None,
|
|
1411
|
+
deferred: bool = False,
|
|
1412
|
+
deferred_group: str | None = None,
|
|
1413
|
+
insert_default: Any = None,
|
|
1414
|
+
init: bool | None = None,
|
|
1415
|
+
repr: bool | None = None, # noqa
|
|
1416
|
+
compare: bool | None = None,
|
|
1417
|
+
primary_key: bool = False,
|
|
1418
|
+
nullable: bool = True,
|
|
1419
|
+
default: Any = None,
|
|
1420
|
+
index: bool = False,
|
|
1421
|
+
unique: bool = False,
|
|
1422
|
+
**kwargs: Any,
|
|
1423
|
+
) -> "Column[Any]":
|
|
1424
|
+
"""DateTime field shortcut function
|
|
1425
|
+
|
|
1426
|
+
Creates a datetime column with enhanced functionality and type safety.
|
|
1427
|
+
|
|
1428
|
+
Args:
|
|
1429
|
+
default_factory: Default value factory function
|
|
1430
|
+
validators: Field validator list
|
|
1431
|
+
deferred: Whether to defer loading
|
|
1432
|
+
deferred_group: Deferred loading group
|
|
1433
|
+
insert_default: Insert-time default value
|
|
1434
|
+
init: Whether to include in __init__
|
|
1435
|
+
repr: Whether to include in __repr__
|
|
1436
|
+
compare: Whether to use for comparison
|
|
1437
|
+
primary_key: Whether field is primary key
|
|
1438
|
+
nullable: Whether field allows NULL values
|
|
1439
|
+
default: Default value
|
|
1440
|
+
index: Whether to create index
|
|
1441
|
+
unique: Whether field is unique
|
|
1442
|
+
**kwargs: Additional type-specific parameters
|
|
1443
|
+
|
|
1444
|
+
Returns:
|
|
1445
|
+
Column descriptor for datetime field
|
|
1446
|
+
"""
|
|
1447
|
+
return column(
|
|
1448
|
+
type="datetime",
|
|
1449
|
+
default_factory=default_factory,
|
|
1450
|
+
validators=validators,
|
|
1451
|
+
deferred=deferred,
|
|
1452
|
+
deferred_group=deferred_group,
|
|
1453
|
+
insert_default=insert_default,
|
|
1454
|
+
init=init,
|
|
1455
|
+
repr=repr,
|
|
1456
|
+
compare=compare,
|
|
1457
|
+
primary_key=primary_key,
|
|
1458
|
+
nullable=nullable,
|
|
1459
|
+
default=default,
|
|
1460
|
+
index=index,
|
|
1461
|
+
unique=unique,
|
|
1462
|
+
**kwargs,
|
|
1463
|
+
)
|
|
1464
|
+
|
|
1465
|
+
|
|
1466
|
+
def numeric_column(
|
|
1467
|
+
*,
|
|
1468
|
+
precision: int | None = None,
|
|
1469
|
+
scale: int | None = None,
|
|
1470
|
+
default_factory: Callable[[], Any] | None = None,
|
|
1471
|
+
validators: list[Any] | None = None,
|
|
1472
|
+
deferred: bool = False,
|
|
1473
|
+
deferred_group: str | None = None,
|
|
1474
|
+
insert_default: Any = None,
|
|
1475
|
+
init: bool | None = None,
|
|
1476
|
+
repr: bool | None = None, # noqa
|
|
1477
|
+
compare: bool | None = None,
|
|
1478
|
+
primary_key: bool = False,
|
|
1479
|
+
nullable: bool = True,
|
|
1480
|
+
default: Any = None,
|
|
1481
|
+
index: bool = False,
|
|
1482
|
+
unique: bool = False,
|
|
1483
|
+
**kwargs: Any,
|
|
1484
|
+
) -> "Column[Any]":
|
|
1485
|
+
"""Numeric field shortcut function
|
|
1486
|
+
|
|
1487
|
+
Creates a numeric/decimal column with enhanced functionality and type safety.
|
|
1488
|
+
|
|
1489
|
+
Args:
|
|
1490
|
+
precision: Total number of digits
|
|
1491
|
+
scale: Number of digits after decimal point
|
|
1492
|
+
default_factory: Default value factory function
|
|
1493
|
+
validators: Field validator list
|
|
1494
|
+
deferred: Whether to defer loading
|
|
1495
|
+
deferred_group: Deferred loading group
|
|
1496
|
+
insert_default: Insert-time default value
|
|
1497
|
+
init: Whether to include in __init__
|
|
1498
|
+
repr: Whether to include in __repr__
|
|
1499
|
+
compare: Whether to use for comparison
|
|
1500
|
+
primary_key: Whether field is primary key
|
|
1501
|
+
nullable: Whether field allows NULL values
|
|
1502
|
+
default: Default value
|
|
1503
|
+
index: Whether to create index
|
|
1504
|
+
unique: Whether field is unique
|
|
1505
|
+
**kwargs: Additional type-specific parameters
|
|
1506
|
+
|
|
1507
|
+
Returns:
|
|
1508
|
+
Column descriptor for numeric field
|
|
1509
|
+
"""
|
|
1510
|
+
return column(
|
|
1511
|
+
type="numeric",
|
|
1512
|
+
precision=precision,
|
|
1513
|
+
scale=scale,
|
|
1514
|
+
default_factory=default_factory,
|
|
1515
|
+
validators=validators,
|
|
1516
|
+
deferred=deferred,
|
|
1517
|
+
deferred_group=deferred_group,
|
|
1518
|
+
insert_default=insert_default,
|
|
1519
|
+
init=init,
|
|
1520
|
+
repr=repr,
|
|
1521
|
+
compare=compare,
|
|
1522
|
+
primary_key=primary_key,
|
|
1523
|
+
nullable=nullable,
|
|
1524
|
+
default=default,
|
|
1525
|
+
index=index,
|
|
1526
|
+
unique=unique,
|
|
1527
|
+
**kwargs,
|
|
1528
|
+
)
|
|
1529
|
+
|
|
1530
|
+
|
|
1531
|
+
def text_column(
|
|
1532
|
+
*,
|
|
1533
|
+
default_factory: Callable[[], Any] | None = None,
|
|
1534
|
+
validators: list[Any] | None = None,
|
|
1535
|
+
deferred: bool = False,
|
|
1536
|
+
deferred_group: str | None = None,
|
|
1537
|
+
insert_default: Any = None,
|
|
1538
|
+
init: bool | None = None,
|
|
1539
|
+
repr: bool | None = None, # noqa
|
|
1540
|
+
compare: bool | None = None,
|
|
1541
|
+
active_history: bool = False,
|
|
1542
|
+
deferred_raiseload: bool | None = None,
|
|
1543
|
+
hash: bool | None = None, # noqa
|
|
1544
|
+
kw_only: bool | None = None,
|
|
1545
|
+
primary_key: bool = False,
|
|
1546
|
+
nullable: bool = True,
|
|
1547
|
+
default: Any = None,
|
|
1548
|
+
index: bool = False,
|
|
1549
|
+
unique: bool = False,
|
|
1550
|
+
**kwargs: Any,
|
|
1551
|
+
) -> "Column[Any]":
|
|
1552
|
+
"""Text field shortcut function (supports all advanced features)
|
|
1553
|
+
|
|
1554
|
+
Creates a text column with full advanced functionality support.
|
|
1555
|
+
|
|
1556
|
+
Args:
|
|
1557
|
+
default_factory: Default value factory function
|
|
1558
|
+
validators: Field validator list
|
|
1559
|
+
deferred: Whether to defer loading
|
|
1560
|
+
deferred_group: Deferred loading group
|
|
1561
|
+
insert_default: Insert-time default value
|
|
1562
|
+
init: Whether to include in __init__
|
|
1563
|
+
repr: Whether to include in __repr__
|
|
1564
|
+
compare: Whether to use for comparison
|
|
1565
|
+
active_history: Whether to enable history tracking
|
|
1566
|
+
deferred_raiseload: Deferred loading exception handling
|
|
1567
|
+
hash: Whether to use for hash calculation
|
|
1568
|
+
kw_only: Whether keyword-only parameter
|
|
1569
|
+
primary_key: Whether field is primary key
|
|
1570
|
+
nullable: Whether field allows NULL values
|
|
1571
|
+
default: Default value
|
|
1572
|
+
index: Whether to create index
|
|
1573
|
+
unique: Whether field is unique
|
|
1574
|
+
**kwargs: Additional type-specific parameters
|
|
1575
|
+
|
|
1576
|
+
Returns:
|
|
1577
|
+
Column descriptor for text field
|
|
1578
|
+
"""
|
|
1579
|
+
return column(
|
|
1580
|
+
type="text",
|
|
1581
|
+
default_factory=default_factory,
|
|
1582
|
+
validators=validators,
|
|
1583
|
+
deferred=deferred,
|
|
1584
|
+
deferred_group=deferred_group,
|
|
1585
|
+
insert_default=insert_default,
|
|
1586
|
+
init=init,
|
|
1587
|
+
repr=repr,
|
|
1588
|
+
compare=compare,
|
|
1589
|
+
active_history=active_history,
|
|
1590
|
+
deferred_raiseload=deferred_raiseload,
|
|
1591
|
+
hash=hash,
|
|
1592
|
+
kw_only=kw_only,
|
|
1593
|
+
primary_key=primary_key,
|
|
1594
|
+
nullable=nullable,
|
|
1595
|
+
default=default,
|
|
1596
|
+
index=index,
|
|
1597
|
+
unique=unique,
|
|
1598
|
+
**kwargs,
|
|
1599
|
+
)
|
|
1600
|
+
|
|
1601
|
+
|
|
1602
|
+
def binary_column(
|
|
1603
|
+
*,
|
|
1604
|
+
length: int | None = None,
|
|
1605
|
+
default_factory: Callable[[], Any] | None = None,
|
|
1606
|
+
validators: list[Any] | None = None,
|
|
1607
|
+
deferred: bool = False,
|
|
1608
|
+
deferred_group: str | None = None,
|
|
1609
|
+
insert_default: Any = None,
|
|
1610
|
+
init: bool | None = None,
|
|
1611
|
+
repr: bool | None = None, # noqa
|
|
1612
|
+
compare: bool | None = None,
|
|
1613
|
+
active_history: bool = False,
|
|
1614
|
+
deferred_raiseload: bool | None = None,
|
|
1615
|
+
hash: bool | None = None, # noqa
|
|
1616
|
+
kw_only: bool | None = None,
|
|
1617
|
+
primary_key: bool = False,
|
|
1618
|
+
nullable: bool = True,
|
|
1619
|
+
default: Any = None,
|
|
1620
|
+
index: bool = False,
|
|
1621
|
+
unique: bool = False,
|
|
1622
|
+
**kwargs: Any,
|
|
1623
|
+
) -> "Column[Any]":
|
|
1624
|
+
"""Binary field shortcut function (supports all advanced features)
|
|
1625
|
+
|
|
1626
|
+
Creates a binary column with full advanced functionality support.
|
|
1627
|
+
|
|
1628
|
+
Args:
|
|
1629
|
+
length: Maximum binary data length
|
|
1630
|
+
default_factory: Default value factory function
|
|
1631
|
+
validators: Field validator list
|
|
1632
|
+
deferred: Whether to defer loading
|
|
1633
|
+
deferred_group: Deferred loading group
|
|
1634
|
+
insert_default: Insert-time default value
|
|
1635
|
+
init: Whether to include in __init__
|
|
1636
|
+
repr: Whether to include in __repr__
|
|
1637
|
+
compare: Whether to use for comparison
|
|
1638
|
+
active_history: Whether to enable history tracking
|
|
1639
|
+
deferred_raiseload: Deferred loading exception handling
|
|
1640
|
+
hash: Whether to use for hash calculation
|
|
1641
|
+
kw_only: Whether keyword-only parameter
|
|
1642
|
+
primary_key: Whether field is primary key
|
|
1643
|
+
nullable: Whether field allows NULL values
|
|
1644
|
+
default: Default value
|
|
1645
|
+
index: Whether to create index
|
|
1646
|
+
unique: Whether field is unique
|
|
1647
|
+
**kwargs: Additional type-specific parameters
|
|
1648
|
+
|
|
1649
|
+
Returns:
|
|
1650
|
+
Column descriptor for binary field
|
|
1651
|
+
"""
|
|
1652
|
+
return column(
|
|
1653
|
+
type="binary",
|
|
1654
|
+
length=length,
|
|
1655
|
+
default_factory=default_factory,
|
|
1656
|
+
validators=validators,
|
|
1657
|
+
deferred=deferred,
|
|
1658
|
+
deferred_group=deferred_group,
|
|
1659
|
+
insert_default=insert_default,
|
|
1660
|
+
init=init,
|
|
1661
|
+
repr=repr,
|
|
1662
|
+
compare=compare,
|
|
1663
|
+
active_history=active_history,
|
|
1664
|
+
deferred_raiseload=deferred_raiseload,
|
|
1665
|
+
hash=hash,
|
|
1666
|
+
kw_only=kw_only,
|
|
1667
|
+
primary_key=primary_key,
|
|
1668
|
+
nullable=nullable,
|
|
1669
|
+
default=default,
|
|
1670
|
+
index=index,
|
|
1671
|
+
unique=unique,
|
|
1672
|
+
**kwargs,
|
|
1673
|
+
)
|
|
1674
|
+
|
|
1675
|
+
|
|
1676
|
+
def _extract_type_params(type_def: TypeDefinition, kwargs: dict) -> dict:
|
|
1677
|
+
"""Extract type construction parameters from kwargs
|
|
1678
|
+
|
|
1679
|
+
Args:
|
|
1680
|
+
type_def: Type definition containing parameter specifications
|
|
1681
|
+
kwargs: Keyword arguments to filter
|
|
1682
|
+
|
|
1683
|
+
Returns:
|
|
1684
|
+
Dictionary of type-specific parameters
|
|
1685
|
+
"""
|
|
1686
|
+
type_param_names = {arg["name"] for arg in type_def["arguments"]}
|
|
1687
|
+
return {k: v for k, v in kwargs.items() if k in type_param_names}
|
|
1688
|
+
|
|
1689
|
+
|
|
1690
|
+
def _store_performance_params(
|
|
1691
|
+
info: dict[str, Any],
|
|
1692
|
+
deferred: bool,
|
|
1693
|
+
deferred_group: str | None,
|
|
1694
|
+
deferred_raiseload: bool | None,
|
|
1695
|
+
active_history: bool,
|
|
1696
|
+
) -> None:
|
|
1697
|
+
"""Store performance optimization related parameters
|
|
1698
|
+
|
|
1699
|
+
Args:
|
|
1700
|
+
info: Info dictionary to store parameters in
|
|
1701
|
+
deferred: Whether field is deferred
|
|
1702
|
+
deferred_group: Deferred loading group
|
|
1703
|
+
deferred_raiseload: Deferred loading exception handling
|
|
1704
|
+
active_history: Whether to enable active history
|
|
1705
|
+
"""
|
|
1706
|
+
performance_params = info.setdefault("_performance", {})
|
|
1707
|
+
|
|
1708
|
+
if deferred:
|
|
1709
|
+
performance_params["deferred"] = True
|
|
1710
|
+
if deferred_group is not None:
|
|
1711
|
+
performance_params["deferred_group"] = deferred_group
|
|
1712
|
+
if deferred_raiseload is not None:
|
|
1713
|
+
performance_params["deferred_raiseload"] = deferred_raiseload
|
|
1714
|
+
|
|
1715
|
+
if active_history:
|
|
1716
|
+
performance_params["active_history"] = True
|
|
1717
|
+
|
|
1718
|
+
|
|
1719
|
+
def _store_codegen_params(
|
|
1720
|
+
info: dict[str, Any],
|
|
1721
|
+
init: bool | None,
|
|
1722
|
+
repr: bool | None, # noqa
|
|
1723
|
+
compare: bool | None,
|
|
1724
|
+
hash: bool | None, # noqa
|
|
1725
|
+
kw_only: bool | None,
|
|
1726
|
+
) -> None:
|
|
1727
|
+
"""Store code generation related parameters
|
|
1728
|
+
|
|
1729
|
+
Args:
|
|
1730
|
+
info: Info dictionary to store parameters in
|
|
1731
|
+
init: Whether to include in __init__
|
|
1732
|
+
repr: Whether to include in __repr__
|
|
1733
|
+
compare: Whether to use for comparison
|
|
1734
|
+
hash: Whether to use for hash calculation
|
|
1735
|
+
kw_only: Whether keyword-only parameter
|
|
1736
|
+
"""
|
|
1737
|
+
codegen_params = info.setdefault("_codegen", {})
|
|
1738
|
+
|
|
1739
|
+
if init is not None:
|
|
1740
|
+
codegen_params["init"] = init
|
|
1741
|
+
if repr is not None:
|
|
1742
|
+
codegen_params["repr"] = repr
|
|
1743
|
+
if compare is not None:
|
|
1744
|
+
codegen_params["compare"] = compare
|
|
1745
|
+
if hash is not None:
|
|
1746
|
+
codegen_params["hash"] = hash
|
|
1747
|
+
if kw_only is not None:
|
|
1748
|
+
codegen_params["kw_only"] = kw_only
|
|
1749
|
+
|
|
1750
|
+
|
|
1751
|
+
def _store_enhanced_params(
|
|
1752
|
+
info: dict[str, Any],
|
|
1753
|
+
default_factory: Callable[[], Any] | None,
|
|
1754
|
+
insert_default: Any,
|
|
1755
|
+
validators: list[Any] | None,
|
|
1756
|
+
) -> None:
|
|
1757
|
+
"""Store functionality enhancement related parameters
|
|
1758
|
+
|
|
1759
|
+
Args:
|
|
1760
|
+
info: Info dictionary to store parameters in
|
|
1761
|
+
default_factory: Default value factory function
|
|
1762
|
+
insert_default: Insert-time default value
|
|
1763
|
+
validators: Field validator list
|
|
1764
|
+
"""
|
|
1765
|
+
enhanced_params = info.setdefault("_enhanced", {})
|
|
1766
|
+
|
|
1767
|
+
if default_factory is not None:
|
|
1768
|
+
enhanced_params["default_factory"] = default_factory
|
|
1769
|
+
if insert_default is not None:
|
|
1770
|
+
enhanced_params["insert_default"] = insert_default
|
|
1771
|
+
if validators is not None:
|
|
1772
|
+
enhanced_params["validators"] = validators
|
|
1773
|
+
|
|
1774
|
+
|
|
1775
|
+
def _resolve_default_value(
|
|
1776
|
+
default: Any,
|
|
1777
|
+
default_factory: Callable[[], Any] | None,
|
|
1778
|
+
insert_default: Any,
|
|
1779
|
+
) -> Any:
|
|
1780
|
+
"""Resolve default value priority: default > default_factory > insert_default
|
|
1781
|
+
|
|
1782
|
+
Args:
|
|
1783
|
+
default: Direct default value
|
|
1784
|
+
default_factory: Default value factory function
|
|
1785
|
+
insert_default: Insert-time default value
|
|
1786
|
+
|
|
1787
|
+
Returns:
|
|
1788
|
+
Resolved default value or None
|
|
1789
|
+
"""
|
|
1790
|
+
if default is not None:
|
|
1791
|
+
return default
|
|
1792
|
+
|
|
1793
|
+
if default_factory is not None:
|
|
1794
|
+
# Wrap as SQLAlchemy compatible callable
|
|
1795
|
+
return lambda: default_factory()
|
|
1796
|
+
|
|
1797
|
+
if insert_default is not None:
|
|
1798
|
+
# Support SQLAlchemy function expressions
|
|
1799
|
+
return insert_default
|
|
1800
|
+
|
|
1801
|
+
return None
|
|
1802
|
+
|
|
1803
|
+
|
|
1804
|
+
# === Validation and Metadata Utilities ===
|
|
1805
|
+
|
|
1806
|
+
|
|
1807
|
+
def get_field_validators(model_class: type, field_name: str) -> list[Any]:
|
|
1808
|
+
"""Get validator list for specified field
|
|
1809
|
+
|
|
1810
|
+
Args:
|
|
1811
|
+
model_class: Model class to inspect
|
|
1812
|
+
field_name: Name of the field
|
|
1813
|
+
|
|
1814
|
+
Returns:
|
|
1815
|
+
List of validator functions for the field
|
|
1816
|
+
"""
|
|
1817
|
+
if hasattr(model_class, "_field_validators"):
|
|
1818
|
+
return model_class._field_validators.get(field_name, []) # noqa
|
|
1819
|
+
return []
|
|
1820
|
+
|
|
1821
|
+
|
|
1822
|
+
def get_model_metadata(model_class: type) -> dict[str, Any]:
|
|
1823
|
+
"""Get complete metadata information for model
|
|
1824
|
+
|
|
1825
|
+
Args:
|
|
1826
|
+
model_class: Model class to inspect
|
|
1827
|
+
|
|
1828
|
+
Returns:
|
|
1829
|
+
Dictionary containing complete model metadata
|
|
1830
|
+
"""
|
|
1831
|
+
metadata = {
|
|
1832
|
+
"model_name": model_class.__name__,
|
|
1833
|
+
"table_name": getattr(model_class.__table__, "name", None) if hasattr(model_class, "__table__") else None,
|
|
1834
|
+
"fields": {},
|
|
1835
|
+
"validators": getattr(model_class, "_field_validators", {}),
|
|
1836
|
+
}
|
|
1837
|
+
|
|
1838
|
+
# Collect field metadata
|
|
1839
|
+
for name in dir(model_class):
|
|
1840
|
+
if name.startswith("_"):
|
|
1841
|
+
continue
|
|
1842
|
+
try:
|
|
1843
|
+
attr = getattr(model_class, name)
|
|
1844
|
+
if hasattr(attr, "get_field_metadata"):
|
|
1845
|
+
metadata["fields"][name] = attr.get_field_metadata()
|
|
1846
|
+
except Exception: # noqa
|
|
1847
|
+
continue
|
|
1848
|
+
|
|
1849
|
+
# Add model configuration information
|
|
1850
|
+
if hasattr(model_class, "Config"):
|
|
1851
|
+
config_attrs = {}
|
|
1852
|
+
config_class = model_class.Config
|
|
1853
|
+
for attr_name in dir(config_class):
|
|
1854
|
+
if not attr_name.startswith("_") and not callable(getattr(config_class, attr_name, None)):
|
|
1855
|
+
try:
|
|
1856
|
+
value = getattr(config_class, attr_name)
|
|
1857
|
+
if not callable(value):
|
|
1858
|
+
config_attrs[attr_name] = (
|
|
1859
|
+
str(value) if hasattr(value, "__iter__") and not isinstance(value, str | bytes) else value
|
|
1860
|
+
)
|
|
1861
|
+
except Exception: # noqa
|
|
1862
|
+
continue
|
|
1863
|
+
if config_attrs:
|
|
1864
|
+
metadata["config"] = config_attrs
|
|
1865
|
+
|
|
1866
|
+
return metadata
|