1
1
"""Helper to create sqlalchemy filters according to filter querystring parameter"""
2
+ import inspect
2
3
import logging
3
4
from typing import (
4
5
Any ,
@@ -133,10 +134,10 @@ def create_filter(self, schema_field: ModelField, model_column, operator, value)
133
134
pydantic_types , userspace_types = self ._separate_types (types )
134
135
135
136
if pydantic_types :
137
+ func = self ._cast_value_with_pydantic
136
138
if isinstance (value , list ):
137
- clear_value , errors = self ._cast_iterable_with_pydantic (pydantic_types , value )
138
- else :
139
- clear_value , errors = self ._cast_value_with_pydantic (pydantic_types , value )
139
+ func = self ._cast_iterable_with_pydantic
140
+ clear_value , errors = func (pydantic_types , value , schema_field )
140
141
141
142
if clear_value is None and userspace_types :
142
143
log .warning ("Filtering by user type values is not properly tested yet. Use this on your own risk." )
@@ -151,7 +152,10 @@ def create_filter(self, schema_field: ModelField, model_column, operator, value)
151
152
152
153
# Если None, при этом поле обязательное (среди типов в аннотации нет None, то кидаем ошибку)
153
154
if clear_value is None and not can_be_none :
154
- raise InvalidType (detail = ", " .join (errors ))
155
+ raise InvalidType (
156
+ detail = ", " .join (errors ),
157
+ pointer = schema_field .name ,
158
+ )
155
159
156
160
return getattr (model_column , self .operator )(clear_value )
157
161
@@ -179,32 +183,65 @@ def _separate_types(self, types: List[Type]) -> Tuple[List[Type], List[Type]]:
179
183
]
180
184
return pydantic_types , userspace_types
181
185
186
+ def _validator_requires_model_field (self , validator : Callable ) -> bool :
187
+ """
188
+ Check if validator accepts the `field` param
189
+
190
+ :param validator:
191
+ :return:
192
+ """
193
+ signature = inspect .signature (validator )
194
+ parameters = signature .parameters
195
+
196
+ if "field" not in parameters :
197
+ return False
198
+
199
+ field_param = parameters ["field" ]
200
+ field_type = field_param .annotation
201
+
202
+ return field_type == "ModelField" or field_type is ModelField
203
+
182
204
def _cast_value_with_pydantic (
183
205
self ,
184
206
types : List [Type ],
185
207
value : Any ,
208
+ schema_field : ModelField ,
186
209
) -> Tuple [Optional [Any ], List [str ]]:
187
210
result_value , errors = None , []
188
211
189
212
for type_to_cast in types :
190
213
for validator in find_validators (type_to_cast , BaseConfig ):
214
+ args = [value ]
215
+ # TODO: some other way to get all the validator's dependencies?
216
+ if self ._validator_requires_model_field (validator ):
217
+ args .append (schema_field )
191
218
try :
192
- result_value = validator (value )
193
- return result_value , errors
219
+ result_value = validator (* args )
194
220
except Exception as ex :
195
221
errors .append (str (ex ))
222
+ else :
223
+ return result_value , errors
196
224
197
225
return None , errors
198
226
199
- def _cast_iterable_with_pydantic (self , types : List [Type ], values : List ) -> Tuple [List , List [str ]]:
227
+ def _cast_iterable_with_pydantic (
228
+ self ,
229
+ types : List [Type ],
230
+ values : List ,
231
+ schema_field : ModelField ,
232
+ ) -> Tuple [List , List [str ]]:
200
233
type_cast_failed = False
201
234
failed_values = []
202
235
203
236
result_values : List [Any ] = []
204
237
errors : List [str ] = []
205
238
206
239
for value in values :
207
- casted_value , cast_errors = self ._cast_value_with_pydantic (types , value )
240
+ casted_value , cast_errors = self ._cast_value_with_pydantic (
241
+ types ,
242
+ value ,
243
+ schema_field ,
244
+ )
208
245
errors .extend (cast_errors )
209
246
210
247
if casted_value is None :
@@ -217,7 +254,7 @@ def _cast_iterable_with_pydantic(self, types: List[Type], values: List) -> Tuple
217
254
218
255
if type_cast_failed :
219
256
msg = f"Can't parse items { failed_values } of value { values } "
220
- raise InvalidFilters (msg )
257
+ raise InvalidFilters (msg , pointer = schema_field . name )
221
258
222
259
return result_values , errors
223
260
0 commit comments