55with safe deferred loading - returning fallback values instead of raising DetachedInstanceError.
66"""
77
8- from typing import Any
8+ from typing import TYPE_CHECKING , Any , Optional , Type , TypeVar
99
1010from sqlalchemy import event
11- from sqlalchemy .orm import ColumnProperty , column_property
11+ from sqlalchemy .orm import column_property
1212from sqlalchemy .orm .attributes import set_committed_value
1313
14+ if TYPE_CHECKING :
15+ from sqlalchemy .orm ._typing import _ORMColumnExprArgument
16+ from sqlalchemy .orm .interfaces import PropComparator
17+ from sqlalchemy .orm .properties import MappedSQLExpression
18+ from sqlalchemy .sql ._typing import _InfoType
19+
20+ _T = TypeVar ("_T" )
21+
1422
1523def deferred_column_property (
16- expression : Any , * , fallback_value : Any , deferred : bool = True , ** kwargs : Any
17- ) -> ColumnProperty :
24+ expression : "_ORMColumnExprArgument[_T]" ,
25+ * additional_expressions : "_ORMColumnExprArgument[Any]" ,
26+ fallback_value : Any ,
27+ group : Optional [str ] = None ,
28+ deferred : bool = True ,
29+ raiseload : bool = False ,
30+ comparator_factory : Optional [Type ["PropComparator[_T]" ]] = None ,
31+ active_history : bool = False ,
32+ expire_on_flush : bool = True ,
33+ info : Optional ["_InfoType" ] = None ,
34+ doc : Optional [str ] = None ,
35+ ) -> "MappedSQLExpression[_T]" :
1836 """
1937 Create a deferred column property that returns a fallback value instead of raising
2038 DetachedInstanceError when accessed on a detached instance.
@@ -24,12 +42,19 @@ def deferred_column_property(
2442
2543 Args:
2644 expression: The SQL expression for the column property
45+ *additional_expressions: Additional SQL expressions for the column property
2746 fallback_value: Value to return when property cannot be loaded (required)
47+ group: A group name for this property when marked as deferred
2848 deferred: Whether to defer loading the property (defaults to True)
29- **kwargs: Additional arguments passed to column_property
49+ raiseload: When True, loading this property will raise an error
50+ comparator_factory: A class which extends PropComparator for custom SQL clause generation
51+ active_history: When True, indicates that the "previous" value should be loaded when replaced
52+ expire_on_flush: Whether this property expires on session flush
53+ info: Optional data dictionary which will be populated into the MapperProperty.info attribute
54+ doc: Optional string that will be applied as the doc on the class-bound descriptor
3055
3156 Returns:
32- A standard ColumnProperty instance with event listeners for fallback handling
57+ A MappedSQLExpression instance with event listeners for fallback handling
3358
3459 Example:
3560 ```python
@@ -46,10 +71,20 @@ def __declare_last__(cls):
4671 )
4772 ```
4873 """
49- kwargs ["deferred" ] = deferred
5074
51- # Create a standard ColumnProperty
52- prop = column_property (expression , ** kwargs )
75+ # Create a standard ColumnProperty with all the parameters
76+ prop = column_property (
77+ expression ,
78+ * additional_expressions ,
79+ group = group ,
80+ deferred = deferred ,
81+ raiseload = raiseload ,
82+ comparator_factory = comparator_factory ,
83+ active_history = active_history ,
84+ expire_on_flush = expire_on_flush ,
85+ info = info ,
86+ doc = doc ,
87+ )
5388
5489 # Store fallback_value as attribute for later use in event setup
5590 prop ._deferred_fallback_value = fallback_value
@@ -78,6 +113,7 @@ def _setup_fallback_listeners(mapper, key: str, fallback_value: Any):
78113 @event .listens_for (class_type , "load" )
79114 def _set_deferred_fallback_on_load (target , context ):
80115 """Set fallback value when object is loaded from database"""
116+ # Only set fallback if key is not in __dict__ (not loaded)
81117 if key not in target .__dict__ :
82118 set_committed_value (target , key , fallback_value )
83119
0 commit comments