1717 load_only ,
1818 noload ,
1919 raiseload ,
20- selectin_polymorphic ,
2120 selectinload ,
2221 subqueryload ,
2322 undefer ,
2423 undefer_group ,
25- with_expression ,
2624)
2725from sqlalchemy .orm .util import AliasedClass
26+ from sqlalchemy .sql .base import ExecutableOption
2827from sqlalchemy .sql .operators import ColumnOperators
2928from sqlalchemy .sql .schema import Column
3029
3534 ModelColumnError ,
3635 SelectOperatorError ,
3736)
38- from sqlalchemy_crud_plus .types import JoinConditions , LoadStrategies , Model
37+ from sqlalchemy_crud_plus .types import JoinConditions , JoinConfig , LoadStrategies , Model
3938
4039_SUPPORTED_FILTERS = {
4140 # Comparison: https://docs.sqlalchemy.org/en/20/core/operators.html#comparison-operators
@@ -181,7 +180,7 @@ def _create_arithmetic_filters(column: Column, op: str, value: dict[str, Any]) -
181180 return arithmetic_filters
182181
183182
184- def _create_and_filters (column : Column , op : str , value : Any ) -> list [ColumnElement | None ]:
183+ def _create_and_filters (column : Column , op : str , value : Any ) -> list [ColumnElement [ Any ] | None ]:
185184 """
186185 Create AND filter expressions.
187186
@@ -197,7 +196,7 @@ def _create_and_filters(column: Column, op: str, value: Any) -> list[ColumnEleme
197196 return and_filters
198197
199198
200- def parse_filters (model : type [Model ] | AliasedClass , ** kwargs ) -> list [ColumnElement ]:
199+ def parse_filters (model : type [Model ] | AliasedClass , ** kwargs ) -> list [ColumnElement [ Any ] ]:
201200 """
202201 Parse filter expressions from keyword arguments.
203202
@@ -218,6 +217,9 @@ def parse_filters(model: type[Model] | AliasedClass, **kwargs) -> list[ColumnEle
218217 if field_name == '__or' and op == '' :
219218 __or__filters = []
220219
220+ if not isinstance (value , dict ):
221+ raise SelectOperatorError ('__or__ filter value must be a dictionary' )
222+
221223 for _key , _value in value .items ():
222224 if '__' not in _key :
223225 _column = get_column (model , _key )
@@ -303,14 +305,16 @@ def apply_sorting(
303305 return stmt
304306
305307
306- def build_load_strategies (model : type [Model ], load_strategies : LoadStrategies | None ) -> list :
308+ def build_load_strategies (model : type [Model ], load_strategies : LoadStrategies | None ) -> list [ ExecutableOption ] :
307309 """
308310 Build relationship loading strategy options.
309311
310312 :param model: SQLAlchemy model class
311313 :param load_strategies: Loading strategies configuration
312314 :return:
313315 """
316+ if load_strategies is None :
317+ return []
314318
315319 strategies_map = {
316320 'contains_eager' : contains_eager ,
@@ -325,10 +329,10 @@ def build_load_strategies(model: type[Model], load_strategies: LoadStrategies |
325329 # Load
326330 'defer' : defer ,
327331 'load_only' : load_only ,
328- 'selectin_polymorphic' : selectin_polymorphic ,
332+ # 'selectin_polymorphic': selectin_polymorphic,
329333 'undefer' : undefer ,
330334 'undefer_group' : undefer_group ,
331- 'with_expression' : with_expression ,
335+ # 'with_expression': with_expression,
332336 }
333337
334338 options = []
@@ -359,7 +363,7 @@ def build_load_strategies(model: type[Model], load_strategies: LoadStrategies |
359363 return options
360364
361365
362- def apply_join_conditions (model : type [Model ], stmt : Select , join_conditions : JoinConditions | None ):
366+ def apply_join_conditions (model : type [Model ], stmt : Select , join_conditions : JoinConditions | None ) -> Select :
363367 """
364368 Apply JOIN conditions to the query statement.
365369
@@ -368,13 +372,24 @@ def apply_join_conditions(model: type[Model], stmt: Select, join_conditions: Joi
368372 :param join_conditions: JOIN conditions configuration
369373 :return:
370374 """
375+ if join_conditions is None :
376+ return stmt
377+
371378 if isinstance (join_conditions , list ):
372- for column in join_conditions :
373- try :
374- attr = getattr (model , column )
375- stmt = stmt .join (attr )
376- except AttributeError :
377- raise ModelColumnError (f'Invalid model column: { column } ' )
379+ for v in join_conditions :
380+ if isinstance (v , str ):
381+ try :
382+ attr = getattr (model , v )
383+ stmt = stmt .join (attr )
384+ except AttributeError :
385+ raise ModelColumnError (f'Invalid model column: { v } ' )
386+ elif isinstance (v , JoinConfig ):
387+ if v .join_type == 'inner' :
388+ stmt = stmt .join (v .model , v .join_on )
389+ elif v .join_type == 'left' :
390+ stmt = stmt .join (v .model , v .join_on , isouter = True )
391+ elif v .join_type == 'full' :
392+ stmt = stmt .join (v .model , v .join_on , full = True )
378393
379394 elif isinstance (join_conditions , dict ):
380395 for column , join_type in join_conditions .items ():
@@ -383,10 +398,10 @@ def apply_join_conditions(model: type[Model], stmt: Select, join_conditions: Joi
383398 raise JoinConditionError (f'Invalid join type: { join_type } , only supports { allowed_join_types } ' )
384399 try :
385400 attr = getattr (model , column )
386- if join_type == 'left' :
387- stmt = stmt .join (attr , isouter = True )
388- elif join_type == 'inner' :
401+ if join_type == 'inner' :
389402 stmt = stmt .join (attr )
403+ elif join_type == 'left' :
404+ stmt = stmt .join (attr , isouter = True )
390405 elif join_type == 'full' :
391406 stmt = stmt .join (attr , full = True )
392407 else :
0 commit comments