@@ -314,8 +314,9 @@ def execute(self):
314
314
if body :
315
315
self ._logger .debug (' body: %s' , body )
316
316
317
+ params = "&" .join ("%s=%s" % (k , v ) for k , v in self .get_query_params ().items ())
317
318
response = self ._connection .request (
318
- self .get_method (), url , headers = headers , params = self . get_query_params () , data = body )
319
+ self .get_method (), url , headers = headers , params = params , data = body )
319
320
320
321
self ._logger .debug ('Received response' )
321
322
self ._logger .debug (' url: %s' , response .url )
@@ -623,7 +624,7 @@ def expand(self, expand):
623
624
def filter (self , filter_val ):
624
625
"""Sets the filter expression."""
625
626
# returns QueryRequest
626
- self ._filter = quote ( filter_val )
627
+ self ._filter = filter_val
627
628
return self
628
629
629
630
# def nav(self, key_value, nav_property):
@@ -993,6 +994,212 @@ def __gt__(self, value):
993
994
return GetEntitySetFilter .format_filter (self ._proprty , 'gt' , value )
994
995
995
996
997
+ class FilterExpression :
998
+ """A class representing named expression of OData $filter"""
999
+
1000
+ def __init__ (self , ** kwargs ):
1001
+ self ._expressions = kwargs
1002
+ self ._other = None
1003
+ self ._operator = None
1004
+
1005
+ @property
1006
+ def expressions (self ):
1007
+ """Get expressions where key is property name with the operator suffix
1008
+ and value is the left hand side operand.
1009
+ """
1010
+
1011
+ return self ._expressions .items ()
1012
+
1013
+ @property
1014
+ def other (self ):
1015
+ """Get an instance of the other operand"""
1016
+
1017
+ return self ._other
1018
+
1019
+ @property
1020
+ def operator (self ):
1021
+ """The other operand"""
1022
+
1023
+ return self ._operator
1024
+
1025
+ def __or__ (self , other ):
1026
+ if self ._other is not None :
1027
+ raise RuntimeError ('The FilterExpression already initialized' )
1028
+
1029
+ self ._other = other
1030
+ self ._operator = "or"
1031
+ return self
1032
+
1033
+ def __and__ (self , other ):
1034
+ if self ._other is not None :
1035
+ raise RuntimeError ('The FilterExpression already initialized' )
1036
+
1037
+ self ._other = other
1038
+ self ._operator = "and"
1039
+ return self
1040
+
1041
+
1042
+ class GetEntitySetFilterChainable :
1043
+ """
1044
+ Example expressions
1045
+ FirstName='Tim'
1046
+ FirstName__contains='Tim'
1047
+ Age__gt=56
1048
+ Age__gte=6
1049
+ Age__lt=78
1050
+ Age__lte=90
1051
+ Age__range=(5,9)
1052
+ FirstName__in=['Tim', 'Bob', 'Sam']
1053
+ FirstName__startswith='Tim'
1054
+ FirstName__endswith='mothy'
1055
+ Addresses__Suburb='Chatswood'
1056
+ Addresses__Suburb__contains='wood'
1057
+ """
1058
+
1059
+ OPERATORS = [
1060
+ 'startswith' ,
1061
+ 'endswith' ,
1062
+ 'lt' ,
1063
+ 'lte' ,
1064
+ 'gt' ,
1065
+ 'gte' ,
1066
+ 'contains' ,
1067
+ 'range' ,
1068
+ 'in' ,
1069
+ 'length' ,
1070
+ 'eq'
1071
+ ]
1072
+
1073
+ def __init__ (self , entity_type , filter_expressions , exprs ):
1074
+ self ._entity_type = entity_type
1075
+ self ._filter_expressions = filter_expressions
1076
+ self ._expressions = exprs
1077
+
1078
+ @property
1079
+ def expressions (self ):
1080
+ """Get expressions as a list of tuples where the first item
1081
+ is a property name with the operator suffix and the second item
1082
+ is a left hand side value.
1083
+ """
1084
+
1085
+ return self ._expressions .items ()
1086
+
1087
+ def proprty_obj (self , name ):
1088
+ """Returns a model property for a particular property"""
1089
+
1090
+ return self ._entity_type .proprty (name )
1091
+
1092
+ def _decode_and_combine_filter_expression (self , filter_expression ):
1093
+ filter_expressions = [self ._decode_expression (expr , val ) for expr , val in filter_expression .expressions ]
1094
+ return self ._combine_expressions (filter_expressions )
1095
+
1096
+ def _process_query_objects (self ):
1097
+ """Processes FilterExpression objects to OData lookups"""
1098
+
1099
+ filter_expressions = []
1100
+
1101
+ for expr in self ._filter_expressions :
1102
+ lhs_expressions = self ._decode_and_combine_filter_expression (expr )
1103
+
1104
+ if expr .other is not None :
1105
+ rhs_expressions = self ._decode_and_combine_filter_expression (expr .other )
1106
+ filter_expressions .append (f'({ lhs_expressions } ) { expr .operator } ({ rhs_expressions } )' )
1107
+ else :
1108
+ filter_expressions .append (lhs_expressions )
1109
+
1110
+ return filter_expressions
1111
+
1112
+ def _process_expressions (self ):
1113
+ filter_expressions = [self ._decode_expression (expr , val ) for expr , val in self .expressions ]
1114
+
1115
+ filter_expressions .extend (self ._process_query_objects ())
1116
+
1117
+ return filter_expressions
1118
+
1119
+ def _decode_expression (self , expr , val ):
1120
+ field = None
1121
+ # field_heirarchy = []
1122
+ operator = 'eq'
1123
+ exprs = expr .split ('__' )
1124
+
1125
+ for part in exprs :
1126
+ if self ._entity_type .has_proprty (part ):
1127
+ field = part
1128
+ # field_heirarchy.append(part)
1129
+ elif part in self .__class__ .OPERATORS :
1130
+ operator = part
1131
+ else :
1132
+ raise ValueError (f'"{ part } " is not a valid property or operator' )
1133
+ # field = '/'.join(field_heirarchy)
1134
+
1135
+ # target_field = self.proprty_obj(field_heirarchy[-1])
1136
+ expression = self ._build_expression (field , operator , val )
1137
+
1138
+ return expression
1139
+
1140
+ # pylint: disable=no-self-use
1141
+ def _combine_expressions (self , expressions ):
1142
+ return ' and ' .join (expressions )
1143
+
1144
+ # pylint: disable=too-many-return-statements, too-many-branches
1145
+ def _build_expression (self , field_name , operator , value ):
1146
+ target_field = self .proprty_obj (field_name )
1147
+
1148
+ if operator not in ['length' , 'in' , 'range' ]:
1149
+ value = target_field .to_literal (value )
1150
+
1151
+ if operator == 'lt' :
1152
+ return f'{ field_name } lt { value } '
1153
+
1154
+ if operator == 'lte' :
1155
+ return f'{ field_name } le { value } '
1156
+
1157
+ if operator == 'gte' :
1158
+ return f'{ field_name } ge { value } '
1159
+
1160
+ if operator == 'gt' :
1161
+ return f'{ field_name } gt { value } '
1162
+
1163
+ if operator == 'startswith' :
1164
+ return f'startswith({ field_name } , { value } ) eq true'
1165
+
1166
+ if operator == 'endswith' :
1167
+ return f'endswith({ field_name } , { value } ) eq true'
1168
+
1169
+ if operator == 'length' :
1170
+ value = int (value )
1171
+ return f'length({ field_name } ) eq { value } '
1172
+
1173
+ if operator in ['contains' ]:
1174
+ return f'substringof({ value } , { field_name } ) eq true'
1175
+
1176
+ if operator == 'range' :
1177
+ if not isinstance (value , (tuple , list )):
1178
+ raise TypeError ('Range must be tuple or list not {}' .format (type (value )))
1179
+
1180
+ if len (value ) != 2 :
1181
+ raise ValueError ('Only two items can be passed in a range.' )
1182
+
1183
+ low_bound = target_field .to_literal (value [0 ])
1184
+ high_bound = target_field .to_literal (value [1 ])
1185
+
1186
+ return f'{ field_name } gte { low_bound } and { field_name } lte { high_bound } '
1187
+
1188
+ if operator == 'in' :
1189
+ literal_values = (f'{ field_name } eq { target_field .to_literal (item )} ' for item in value )
1190
+ return ' or ' .join (literal_values )
1191
+
1192
+ if operator == 'eq' :
1193
+ return f'{ field_name } eq { value } '
1194
+
1195
+ raise ValueError (f'Invalid expression { operator } ' )
1196
+
1197
+ def __str__ (self ):
1198
+ expressions = self ._process_expressions ()
1199
+ result = self ._combine_expressions (expressions )
1200
+ return quote (result )
1201
+
1202
+
996
1203
class GetEntitySetRequest (QueryRequest ):
997
1204
"""GET on EntitySet"""
998
1205
@@ -1005,6 +1212,19 @@ def __getattr__(self, name):
1005
1212
proprty = self ._entity_type .proprty (name )
1006
1213
return GetEntitySetFilter (proprty )
1007
1214
1215
+ def _set_filter (self , filter_val ):
1216
+ filter_text = self ._filter + ' and ' if self ._filter else ''
1217
+ filter_text += filter_val
1218
+ self ._filter = filter_text
1219
+
1220
+ def filter (self , * args , ** kwargs ):
1221
+ if args and len (args ) == 1 and isinstance (args [0 ], str ):
1222
+ self ._filter = args [0 ]
1223
+ else :
1224
+ self ._set_filter (str (GetEntitySetFilterChainable (self ._entity_type , args , kwargs )))
1225
+
1226
+ return self
1227
+
1008
1228
1009
1229
class EntitySetProxy :
1010
1230
"""EntitySet Proxy"""
0 commit comments