sqladmin 0.22.0__py3-none-any.whl → 0.23.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.
sqladmin/filters.py CHANGED
@@ -20,20 +20,20 @@ from sqladmin._types import MODEL_ATTR
20
20
  try:
21
21
  import uuid
22
22
 
23
- from sqlalchemy import Uuid
23
+ from sqlalchemy import Uuid # type: ignore[attr-defined]
24
24
 
25
25
  HAS_UUID_SUPPORT = True
26
26
  except ImportError:
27
27
  # Fallback for SQLAlchemy < 2.0
28
28
  HAS_UUID_SUPPORT = False
29
- Uuid = None
29
+ Uuid = None # type: ignore[misc, assignment]
30
30
 
31
31
 
32
32
  def get_parameter_name(column: MODEL_ATTR) -> str:
33
33
  if isinstance(column, str):
34
34
  return column
35
- else:
36
- return column.key
35
+
36
+ return column.key
37
37
 
38
38
 
39
39
  def prettify_attribute_name(name: str) -> str:
@@ -76,7 +76,10 @@ class BooleanFilter:
76
76
  self.parameter_name = parameter_name or get_parameter_name(column)
77
77
 
78
78
  async def lookups(
79
- self, request: Request, model: Any, run_query: Callable[[Select], Any]
79
+ self,
80
+ request: Request,
81
+ model: Any,
82
+ run_query: Callable[[Select], Any],
80
83
  ) -> List[Tuple[str, str]]:
81
84
  return [
82
85
  ("all", "All"),
@@ -88,10 +91,11 @@ class BooleanFilter:
88
91
  column_obj = get_column_obj(self.column, model)
89
92
  if value == "true":
90
93
  return query.filter(column_obj.is_(True))
91
- elif value == "false":
94
+
95
+ if value == "false":
92
96
  return query.filter(column_obj.is_(False))
93
- else:
94
- return query
97
+
98
+ return query
95
99
 
96
100
 
97
101
  class AllUniqueStringValuesFilter:
@@ -108,7 +112,10 @@ class AllUniqueStringValuesFilter:
108
112
  self.parameter_name = parameter_name or get_parameter_name(column)
109
113
 
110
114
  async def lookups(
111
- self, request: Request, model: Any, run_query: Callable[[Select], Any]
115
+ self,
116
+ request: Request,
117
+ model: Any,
118
+ run_query: Callable[[Select], Any],
112
119
  ) -> List[Tuple[str, str]]:
113
120
  column_obj = get_column_obj(self.column, model)
114
121
 
@@ -141,7 +148,10 @@ class StaticValuesFilter:
141
148
  self.values = values
142
149
 
143
150
  async def lookups(
144
- self, request: Request, model: Any, run_query: Callable[[Select], Any]
151
+ self,
152
+ request: Request,
153
+ model: Any,
154
+ run_query: Callable[[Select], Any],
145
155
  ) -> List[Tuple[str, str]]:
146
156
  return [("", "All")] + self.values
147
157
 
@@ -170,13 +180,18 @@ class ForeignKeyFilter:
170
180
  self.parameter_name = parameter_name or get_parameter_name(foreign_key)
171
181
 
172
182
  async def lookups(
173
- self, request: Request, model: Any, run_query: Callable[[Select], Any]
183
+ self,
184
+ request: Request,
185
+ model: Any,
186
+ run_query: Callable[[Select], Any],
174
187
  ) -> List[Tuple[str, str]]:
175
188
  foreign_key_obj = get_column_obj(self.foreign_key, model)
176
189
  if self.foreign_model is None and isinstance(self.foreign_display_field, str):
177
190
  raise ValueError("foreign_model is required for string foreign key filters")
178
191
  if self.foreign_model is None:
179
- assert not isinstance(self.foreign_display_field, str)
192
+ if isinstance(self.foreign_display_field, str):
193
+ raise ValueError("foreign_model should not be string")
194
+
180
195
  foreign_display_field_obj = self.foreign_display_field
181
196
  else:
182
197
  foreign_display_field_obj = get_column_obj(
@@ -227,22 +242,24 @@ class OperationColumnFilter:
227
242
  ("starts_with", "Starts with"),
228
243
  ("ends_with", "Ends with"),
229
244
  ]
230
- elif self._is_numeric_type(column_obj):
245
+
246
+ if self._is_numeric_type(column_obj):
231
247
  return [
232
248
  ("equals", "Equals"),
233
249
  ("greater_than", "Greater than"),
234
250
  ("less_than", "Less than"),
235
251
  ]
236
- elif self._is_uuid_type(column_obj):
252
+
253
+ if self._is_uuid_type(column_obj):
237
254
  return [
238
255
  ("equals", "Equals"),
239
256
  ("contains", "Contains"),
240
257
  ("starts_with", "Starts with"),
241
258
  ]
242
- else:
243
- return [
244
- ("equals", "Equals"),
245
- ]
259
+
260
+ return [
261
+ ("equals", "Equals"),
262
+ ]
246
263
 
247
264
  def get_operation_options_for_model(self, model: Any) -> List[Tuple[str, str]]:
248
265
  """Return operation options based on column type for given model"""
@@ -269,38 +286,45 @@ class OperationColumnFilter:
269
286
 
270
287
  column_type = column_obj.type
271
288
 
272
- try:
273
- if isinstance(column_type, (String, Text, _Binary)):
274
- return str(value)
275
-
276
- if isinstance(column_type, (Integer, BigInteger, SmallInteger)):
277
- return int(value)
289
+ converters = [
290
+ ((String, Text, _Binary), str),
291
+ ((Integer, BigInteger, SmallInteger), int),
292
+ ((Numeric, Float), float),
293
+ ]
278
294
 
279
- if isinstance(column_type, (Numeric, Float)):
280
- return float(value)
295
+ try:
296
+ for types, converter in converters:
297
+ if isinstance(column_type, types):
298
+ return converter(value)
281
299
 
282
- # UUID support for SQLAlchemy 2.0+
283
300
  if HAS_UUID_SUPPORT and isinstance(column_type, Uuid):
284
- # For contains/starts_with operations, keep as string for LIKE queries
285
- if operation in ("contains", "starts_with"):
286
- return str(value.strip())
287
- # For equals operation, validate and convert to UUID
288
- return uuid.UUID(value.strip())
301
+ return (
302
+ str(value.strip())
303
+ if operation in ("contains", "starts_with")
304
+ else uuid.UUID(value.strip())
305
+ )
289
306
 
290
307
  except (ValueError, TypeError):
291
308
  return None
292
309
 
293
- return str(value)
310
+ return value
294
311
 
295
312
  async def lookups(
296
- self, request: Request, model: Any, run_query: Callable[[Select], Any]
313
+ self,
314
+ request: Request,
315
+ model: Any,
316
+ run_query: Callable[[Select], Any],
297
317
  ) -> List[Tuple[str, str]]:
298
318
  # This method is not used for has_operator=True filters
299
319
  # The UI uses get_operation_options_for_model instead
300
320
  return []
301
321
 
302
322
  async def get_filtered_query(
303
- self, query: Select, operation: str, value: Any, model: Any
323
+ self,
324
+ query: Select,
325
+ operation: str,
326
+ value: Any,
327
+ model: Any,
304
328
  ) -> Select:
305
329
  """Handle filtering with separate operation and value parameters"""
306
330
  if not value or value == "" or not operation:
@@ -308,7 +332,9 @@ class OperationColumnFilter:
308
332
 
309
333
  column_obj = get_column_obj(self.column, model)
310
334
  converted_value = self._convert_value_for_column(
311
- str(value).strip(), column_obj, operation
335
+ str(value).strip(),
336
+ column_obj,
337
+ operation,
312
338
  )
313
339
 
314
340
  if converted_value is None:
@@ -319,22 +345,25 @@ class OperationColumnFilter:
319
345
  # For UUID, cast to text for LIKE operations
320
346
  search_value = f"%{str(value).strip()}%"
321
347
  return query.filter(column_obj.cast(String).ilike(search_value))
322
- else:
323
- return query.filter(column_obj.ilike(f"%{str(value).strip()}%"))
324
- elif operation == "equals":
348
+
349
+ return query.filter(column_obj.ilike(f"%{str(value).strip()}%"))
350
+
351
+ if operation == "equals":
325
352
  return query.filter(column_obj == converted_value)
326
- elif operation == "starts_with":
353
+
354
+ if operation == "starts_with":
327
355
  if self._is_uuid_type(column_obj):
328
356
  # For UUID, cast to text for LIKE operations
329
357
  search_value = f"{str(value).strip()}%"
330
358
  return query.filter(column_obj.cast(String).ilike(search_value))
331
- else:
332
- return query.filter(column_obj.startswith(str(value).strip()))
333
- elif operation == "ends_with":
359
+
360
+ return query.filter(column_obj.startswith(str(value).strip()))
361
+
362
+ if operation == "ends_with":
334
363
  return query.filter(column_obj.endswith(str(value).strip()))
335
- elif operation == "greater_than":
364
+ if operation == "greater_than":
336
365
  return query.filter(column_obj > converted_value)
337
- elif operation == "less_than":
366
+ if operation == "less_than":
338
367
  return query.filter(column_obj < converted_value)
339
368
 
340
369
  return query
sqladmin/formatters.py CHANGED
@@ -11,7 +11,7 @@ def empty_formatter(value: Any) -> str:
11
11
  def bool_formatter(value: bool) -> Markup:
12
12
  """Return check icon if value is `True` or X otherwise."""
13
13
  icon_class = "fa-check text-success" if value else "fa-times text-danger"
14
- return Markup(f"<i class='fa {icon_class}'></i>")
14
+ return Markup("<i class='fa {}'></i>").format(icon_class)
15
15
 
16
16
 
17
17
  BASE_FORMATTERS = {
sqladmin/forms.py CHANGED
@@ -1,6 +1,9 @@
1
+ # mypy: disable-error-code="return-value"
2
+
1
3
  """
2
4
  The converters are from Flask-Admin project.
3
5
  """
6
+
4
7
  from __future__ import annotations
5
8
 
6
9
  import enum
@@ -24,7 +27,6 @@ from sqlalchemy.orm import (
24
27
  sessionmaker,
25
28
  )
26
29
  from sqlalchemy.sql.elements import Label
27
- from sqlalchemy.sql.schema import Column
28
30
  from wtforms import (
29
31
  BooleanField,
30
32
  DecimalField,
@@ -75,11 +77,9 @@ else:
75
77
 
76
78
 
77
79
  class Validator(Protocol):
78
- def __init__(self, *args: Any, **kwargs: Any) -> None:
79
- ... # pragma: no cover
80
+ def __init__(self, *args: Any, **kwargs: Any) -> None: ... # pragma: no cover
80
81
 
81
- def __call__(self, form: Form, field: Field) -> None:
82
- ... # pragma: no cover
82
+ def __call__(self, form: Form, field: Field) -> None: ... # pragma: no cover
83
83
 
84
84
 
85
85
  class ConverterCallable(Protocol):
@@ -88,8 +88,7 @@ class ConverterCallable(Protocol):
88
88
  model: type,
89
89
  prop: MODEL_PROPERTY,
90
90
  kwargs: dict[str, Any],
91
- ) -> UnboundField:
92
- ... # pragma: no cover
91
+ ) -> UnboundField: ... # pragma: no cover
93
92
 
94
93
 
95
94
  T_CC = TypeVar("T_CC", bound=ConverterCallable)
@@ -163,9 +162,14 @@ class ModelConverterBase:
163
162
  return kwargs
164
163
 
165
164
  def _prepare_column(
166
- self, prop: ColumnProperty, form_include_pk: bool, kwargs: dict
165
+ self,
166
+ prop: ColumnProperty,
167
+ form_include_pk: bool,
168
+ kwargs: dict,
167
169
  ) -> Union[dict, None]:
168
- assert len(prop.columns) == 1, "Multiple-column properties not supported"
170
+ if len(prop.columns) != 1:
171
+ raise NotImplementedError("Multiple-column properties are not supported")
172
+
169
173
  column = prop.columns[0]
170
174
 
171
175
  if (column.primary_key or column.foreign_keys) and not form_include_pk:
@@ -210,7 +214,7 @@ class ModelConverterBase:
210
214
  loader: QueryAjaxModelLoader | None = None,
211
215
  ) -> dict:
212
216
  nullable = True
213
- for pair in prop.local_remote_pairs:
217
+ for pair in prop.local_remote_pairs or []:
214
218
  if not pair[0].nullable:
215
219
  nullable = False
216
220
 
@@ -290,9 +294,9 @@ class ModelConverterBase:
290
294
  form_include_pk: bool,
291
295
  label: str | None = None,
292
296
  override: type[Field] | None = None,
293
- form_ajax_refs: dict[str, QueryAjaxModelLoader] = {},
297
+ form_ajax_refs: dict[str, QueryAjaxModelLoader] | None = None,
294
298
  ) -> UnboundField:
295
- loader = form_ajax_refs.get(prop.key)
299
+ loader = (form_ajax_refs or {}).get(prop.key)
296
300
  kwargs = await self._prepare_kwargs(
297
301
  prop=prop,
298
302
  session_maker=session_maker,
@@ -307,7 +311,9 @@ class ModelConverterBase:
307
311
  return None
308
312
 
309
313
  if override is not None:
310
- assert issubclass(override, Field)
314
+ if not issubclass(override, Field):
315
+ raise TypeError("Expected Field, got %s" % type(override))
316
+
311
317
  return override(**kwargs)
312
318
 
313
319
  multiple = (
@@ -317,10 +323,8 @@ class ModelConverterBase:
317
323
  )
318
324
 
319
325
  if loader:
320
- if multiple:
321
- return AjaxSelectMultipleField(loader, **kwargs)
322
- else:
323
- return AjaxSelectField(loader, **kwargs)
326
+ field = AjaxSelectMultipleField if multiple else AjaxSelectField
327
+ return field(loader, **kwargs)
324
328
 
325
329
  converter = self.get_converter(prop=prop)
326
330
  return converter(model=model, prop=prop, kwargs=kwargs)
@@ -333,14 +337,17 @@ class ModelConverter(ModelConverterBase):
333
337
  @staticmethod
334
338
  def _string_common(prop: ColumnProperty) -> list[Validator]:
335
339
  li = []
336
- column: Column = prop.columns[0]
337
- if isinstance(column.type.length, int) and column.type.length:
338
- li.append(validators.Length(max=column.type.length))
340
+ column = prop.columns[0]
341
+ if isinstance(column.type.length, int) and column.type.length: # type: ignore[attr-defined]
342
+ li.append(validators.Length(max=column.type.length)) # type: ignore[attr-defined]
339
343
  return li
340
344
 
341
345
  @converts("String", "CHAR") # includes Unicode
342
346
  def conv_string(
343
- self, model: type, prop: ColumnProperty, kwargs: dict[str, Any]
347
+ self,
348
+ model: type,
349
+ prop: ColumnProperty,
350
+ kwargs: dict[str, Any],
344
351
  ) -> UnboundField:
345
352
  extra_validators = self._string_common(prop)
346
353
  kwargs.setdefault("validators", [])
@@ -349,7 +356,10 @@ class ModelConverter(ModelConverterBase):
349
356
 
350
357
  @converts("Text", "LargeBinary", "Binary") # includes UnicodeText
351
358
  def conv_text(
352
- self, model: type, prop: ColumnProperty, kwargs: dict[str, Any]
359
+ self,
360
+ model: type,
361
+ prop: ColumnProperty,
362
+ kwargs: dict[str, Any],
353
363
  ) -> UnboundField:
354
364
  kwargs.setdefault("validators", [])
355
365
  extra_validators = self._string_common(prop)
@@ -358,7 +368,10 @@ class ModelConverter(ModelConverterBase):
358
368
 
359
369
  @converts("Boolean", "dialects.mssql.base.BIT")
360
370
  def conv_boolean(
361
- self, model: type, prop: ColumnProperty, kwargs: dict[str, Any]
371
+ self,
372
+ model: type,
373
+ prop: ColumnProperty,
374
+ kwargs: dict[str, Any],
362
375
  ) -> UnboundField:
363
376
  if not prop.columns[0].nullable:
364
377
  kwargs.setdefault("render_kw", {})
@@ -372,27 +385,42 @@ class ModelConverter(ModelConverterBase):
372
385
 
373
386
  @converts("Date")
374
387
  def conv_date(
375
- self, model: type, prop: ColumnProperty, kwargs: dict[str, Any]
388
+ self,
389
+ model: type,
390
+ prop: ColumnProperty,
391
+ kwargs: dict[str, Any],
376
392
  ) -> UnboundField:
377
393
  return DateField(**kwargs)
378
394
 
379
395
  @converts("Time")
380
396
  def conv_time(
381
- self, model: type, prop: ColumnProperty, kwargs: dict[str, Any]
397
+ self,
398
+ model: type,
399
+ prop: ColumnProperty,
400
+ kwargs: dict[str, Any],
382
401
  ) -> UnboundField:
383
402
  return TimeField(**kwargs)
384
403
 
385
404
  @converts("DateTime")
386
405
  def conv_datetime(
387
- self, model: type, prop: ColumnProperty, kwargs: dict[str, Any]
406
+ self,
407
+ model: type,
408
+ prop: ColumnProperty,
409
+ kwargs: dict[str, Any],
388
410
  ) -> UnboundField:
389
411
  return DateTimeField(**kwargs)
390
412
 
391
413
  @converts("Enum")
392
414
  def conv_enum(
393
- self, model: type, prop: ColumnProperty, kwargs: dict[str, Any]
415
+ self,
416
+ model: type,
417
+ prop: ColumnProperty,
418
+ kwargs: dict[str, Any],
394
419
  ) -> UnboundField:
395
- available_choices = [(e, e) for e in prop.columns[0].type.enums]
420
+ available_choices = [
421
+ (e, e)
422
+ for e in prop.columns[0].type.enums # type: ignore[attr-defined]
423
+ ]
396
424
  accepted_values = [choice[0] for choice in available_choices]
397
425
 
398
426
  if prop.columns[0].nullable:
@@ -410,13 +438,19 @@ class ModelConverter(ModelConverterBase):
410
438
 
411
439
  @converts("Integer") # includes BigInteger and SmallInteger
412
440
  def conv_integer(
413
- self, model: type, prop: ColumnProperty, kwargs: dict[str, Any]
441
+ self,
442
+ model: type,
443
+ prop: ColumnProperty,
444
+ kwargs: dict[str, Any],
414
445
  ) -> UnboundField:
415
446
  return IntegerField(**kwargs)
416
447
 
417
448
  @converts("Numeric") # includes DECIMAL, Float/FLOAT, REAL, and DOUBLE
418
449
  def conv_decimal(
419
- self, model: type, prop: ColumnProperty, kwargs: dict[str, Any]
450
+ self,
451
+ model: type,
452
+ prop: ColumnProperty,
453
+ kwargs: dict[str, Any],
420
454
  ) -> UnboundField:
421
455
  # override default decimal places limit, use database defaults instead
422
456
  kwargs.setdefault("places", None)
@@ -424,13 +458,19 @@ class ModelConverter(ModelConverterBase):
424
458
 
425
459
  @converts("JSON", "JSONB")
426
460
  def conv_json(
427
- self, model: type, prop: ColumnProperty, kwargs: dict[str, Any]
461
+ self,
462
+ model: type,
463
+ prop: ColumnProperty,
464
+ kwargs: dict[str, Any],
428
465
  ) -> UnboundField:
429
466
  return JSONField(**kwargs)
430
467
 
431
468
  @converts("Interval")
432
469
  def conv_interval(
433
- self, model: type, prop: ColumnProperty, kwargs: dict[str, Any]
470
+ self,
471
+ model: type,
472
+ prop: ColumnProperty,
473
+ kwargs: dict[str, Any],
434
474
  ) -> UnboundField:
435
475
  kwargs["render_kw"]["placeholder"] = "Like: 1 day 1:25:33.652"
436
476
  return IntervalField(**kwargs)
@@ -441,7 +481,10 @@ class ModelConverter(ModelConverterBase):
441
481
  "sqlalchemy_utils.types.ip_address.IPAddressType",
442
482
  )
443
483
  def conv_ip_address(
444
- self, model: type, prop: ColumnProperty, kwargs: dict[str, Any]
484
+ self,
485
+ model: type,
486
+ prop: ColumnProperty,
487
+ kwargs: dict[str, Any],
445
488
  ) -> UnboundField:
446
489
  kwargs.setdefault("validators", [])
447
490
  kwargs["validators"].append(validators.IPAddress(ipv4=True, ipv6=True))
@@ -452,7 +495,10 @@ class ModelConverter(ModelConverterBase):
452
495
  "sqlalchemy.dialects.postgresql.types.MACADDR",
453
496
  )
454
497
  def conv_mac_address(
455
- self, model: type, prop: ColumnProperty, kwargs: dict[str, Any]
498
+ self,
499
+ model: type,
500
+ prop: ColumnProperty,
501
+ kwargs: dict[str, Any],
456
502
  ) -> UnboundField:
457
503
  kwargs.setdefault("validators", [])
458
504
  kwargs["validators"].append(validators.MacAddress())
@@ -465,23 +511,33 @@ class ModelConverter(ModelConverterBase):
465
511
  "sqlalchemy_utils.types.uuid.UUIDType",
466
512
  )
467
513
  def conv_uuid(
468
- self, model: type, prop: ColumnProperty, kwargs: dict[str, Any]
514
+ self,
515
+ model: type,
516
+ prop: ColumnProperty,
517
+ kwargs: dict[str, Any],
469
518
  ) -> UnboundField:
470
519
  kwargs.setdefault("validators", [])
471
520
  kwargs["validators"].append(validators.UUID())
472
521
  return StringField(**kwargs)
473
522
 
474
523
  @converts(
475
- "sqlalchemy.dialects.postgresql.base.ARRAY", "sqlalchemy.sql.sqltypes.ARRAY"
524
+ "sqlalchemy.dialects.postgresql.base.ARRAY",
525
+ "sqlalchemy.sql.sqltypes.ARRAY",
476
526
  )
477
- def conv_ARRAY(
478
- self, model: type, prop: ColumnProperty, kwargs: dict[str, Any]
527
+ def conv_array(
528
+ self,
529
+ model: type,
530
+ prop: ColumnProperty,
531
+ kwargs: dict[str, Any],
479
532
  ) -> UnboundField:
480
533
  return Select2TagsField(**kwargs)
481
534
 
482
535
  @converts("sqlalchemy_utils.types.email.EmailType")
483
536
  def conv_email(
484
- self, model: type, prop: ColumnProperty, kwargs: dict[str, Any]
537
+ self,
538
+ model: type,
539
+ prop: ColumnProperty,
540
+ kwargs: dict[str, Any],
485
541
  ) -> UnboundField:
486
542
  kwargs.setdefault("validators", [])
487
543
  kwargs["validators"].append(validators.Email())
@@ -489,7 +545,10 @@ class ModelConverter(ModelConverterBase):
489
545
 
490
546
  @converts("sqlalchemy_utils.types.url.URLType")
491
547
  def conv_url(
492
- self, model: type, prop: ColumnProperty, kwargs: dict[str, Any]
548
+ self,
549
+ model: type,
550
+ prop: ColumnProperty,
551
+ kwargs: dict[str, Any],
493
552
  ) -> UnboundField:
494
553
  kwargs.setdefault("validators", [])
495
554
  kwargs["validators"].append(validators.URL())
@@ -497,7 +556,10 @@ class ModelConverter(ModelConverterBase):
497
556
 
498
557
  @converts("sqlalchemy_utils.types.currency.CurrencyType")
499
558
  def conv_currency(
500
- self, model: type, prop: ColumnProperty, kwargs: dict[str, Any]
559
+ self,
560
+ model: type,
561
+ prop: ColumnProperty,
562
+ kwargs: dict[str, Any],
501
563
  ) -> UnboundField:
502
564
  kwargs.setdefault("validators", [])
503
565
  kwargs["validators"].append(CurrencyValidator())
@@ -505,17 +567,23 @@ class ModelConverter(ModelConverterBase):
505
567
 
506
568
  @converts("sqlalchemy_utils.types.timezone.TimezoneType")
507
569
  def conv_timezone(
508
- self, model: type, prop: ColumnProperty, kwargs: dict[str, Any]
570
+ self,
571
+ model: type,
572
+ prop: ColumnProperty,
573
+ kwargs: dict[str, Any],
509
574
  ) -> UnboundField:
510
575
  kwargs.setdefault("validators", [])
511
576
  kwargs["validators"].append(
512
- TimezoneValidator(coerce_function=prop.columns[0].type._coerce)
577
+ TimezoneValidator(coerce_function=prop.columns[0].type._coerce) # type: ignore[attr-defined]
513
578
  )
514
579
  return StringField(**kwargs)
515
580
 
516
581
  @converts("sqlalchemy_utils.types.phone_number.PhoneNumberType")
517
582
  def conv_phone_number(
518
- self, model: type, prop: ColumnProperty, kwargs: dict[str, Any]
583
+ self,
584
+ model: type,
585
+ prop: ColumnProperty,
586
+ kwargs: dict[str, Any],
519
587
  ) -> UnboundField:
520
588
  kwargs.setdefault("validators", [])
521
589
  kwargs["validators"].append(PhoneNumberValidator())
@@ -523,7 +591,10 @@ class ModelConverter(ModelConverterBase):
523
591
 
524
592
  @converts("sqlalchemy_utils.types.color.ColorType")
525
593
  def conv_color(
526
- self, model: type, prop: ColumnProperty, kwargs: dict[str, Any]
594
+ self,
595
+ model: type,
596
+ prop: ColumnProperty,
597
+ kwargs: dict[str, Any],
527
598
  ) -> UnboundField:
528
599
  kwargs.setdefault("validators", [])
529
600
  kwargs["validators"].append(ColorValidator())
@@ -532,7 +603,10 @@ class ModelConverter(ModelConverterBase):
532
603
  @converts("sqlalchemy_utils.types.choice.ChoiceType")
533
604
  @no_type_check
534
605
  def convert_choice_type(
535
- self, model: type, prop: ColumnProperty, kwargs: dict[str, Any]
606
+ self,
607
+ model: type,
608
+ prop: ColumnProperty,
609
+ kwargs: dict[str, Any],
536
610
  ) -> UnboundField:
537
611
  available_choices = []
538
612
  column = prop.columns[0]
@@ -561,32 +635,47 @@ class ModelConverter(ModelConverterBase):
561
635
 
562
636
  @converts("fastapi_storages.integrations.sqlalchemy.FileType")
563
637
  def conv_file(
564
- self, model: type, prop: ColumnProperty, kwargs: dict[str, Any]
638
+ self,
639
+ model: type,
640
+ prop: ColumnProperty,
641
+ kwargs: dict[str, Any],
565
642
  ) -> UnboundField:
566
643
  return FileField(**kwargs)
567
644
 
568
645
  @converts("fastapi_storages.integrations.sqlalchemy.ImageType")
569
646
  def conv_image(
570
- self, model: type, prop: ColumnProperty, kwargs: dict[str, Any]
647
+ self,
648
+ model: type,
649
+ prop: ColumnProperty,
650
+ kwargs: dict[str, Any],
571
651
  ) -> UnboundField:
572
652
  return FileField(**kwargs)
573
653
 
574
654
  @converts("ONETOONE")
575
655
  def conv_one_to_one(
576
- self, model: type, prop: RelationshipProperty, kwargs: dict[str, Any]
656
+ self,
657
+ model: type,
658
+ prop: RelationshipProperty,
659
+ kwargs: dict[str, Any],
577
660
  ) -> UnboundField:
578
661
  kwargs["allow_blank"] = True
579
662
  return QuerySelectField(**kwargs)
580
663
 
581
664
  @converts("MANYTOONE")
582
665
  def conv_many_to_one(
583
- self, model: type, prop: RelationshipProperty, kwargs: dict[str, Any]
666
+ self,
667
+ model: type,
668
+ prop: RelationshipProperty,
669
+ kwargs: dict[str, Any],
584
670
  ) -> UnboundField:
585
671
  return QuerySelectField(**kwargs)
586
672
 
587
673
  @converts("MANYTOMANY", "ONETOMANY")
588
674
  def conv_many_to_many(
589
- self, model: type, prop: RelationshipProperty, kwargs: dict[str, Any]
675
+ self,
676
+ model: type,
677
+ prop: RelationshipProperty,
678
+ kwargs: dict[str, Any],
590
679
  ) -> UnboundField:
591
680
  return QuerySelectMultipleField(**kwargs)
592
681