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/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