From 9300047bf4264f1b816ee46c5b0880e722caaa16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9C=D0=B0=D0=BA=D1=81=D0=B8=D0=BC?= Date: Thu, 15 Aug 2024 09:37:47 +0300 Subject: [PATCH 1/6] Added custom relationships statements form --- sqladmin/forms.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/sqladmin/forms.py b/sqladmin/forms.py index 00daa7d1..116b12af 100644 --- a/sqladmin/forms.py +++ b/sqladmin/forms.py @@ -16,7 +16,7 @@ ) import anyio -from sqlalchemy import Boolean, select +from sqlalchemy import Boolean, select, Select from sqlalchemy import inspect as sqlalchemy_inspect from sqlalchemy.orm import ( ColumnProperty, @@ -109,6 +109,11 @@ def _inner(func: T_CC) -> T_CC: class ModelConverterBase: + relationships_statements: dict[str, Select] = {} + """ + Select statement for relationships. + """ + _converters: dict[str, ConverterCallable] = {} def __init__(self) -> None: @@ -229,7 +234,10 @@ async def _prepare_select_options( session_maker: sessionmaker, ) -> list[tuple[str, Any]]: target_model = prop.mapper.class_ - stmt = select(target_model) + if prop.key in self.relationships_statements: + stmt = self.relationships_statements[prop.key] + else: + stmt = select(target_model) if is_async_session_maker(session_maker): async with session_maker() as session: From 464db2679d64396269570b85617c485a055a04f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9C=D0=B0=D0=BA=D1=81=D0=B8=D0=BC?= Date: Thu, 15 Aug 2024 09:41:49 +0300 Subject: [PATCH 2/6] Fix formatting --- sqladmin/forms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sqladmin/forms.py b/sqladmin/forms.py index 116b12af..2fa260d1 100644 --- a/sqladmin/forms.py +++ b/sqladmin/forms.py @@ -16,7 +16,7 @@ ) import anyio -from sqlalchemy import Boolean, select, Select +from sqlalchemy import Select, Boolean, select from sqlalchemy import inspect as sqlalchemy_inspect from sqlalchemy.orm import ( ColumnProperty, From 6a16cc60c9a7cce3744e1d5edc657fb1369064b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9C=D0=B0=D0=BA=D1=81=D0=B8=D0=BC?= Date: Thu, 15 Aug 2024 09:48:04 +0300 Subject: [PATCH 3/6] Fix formatting --- sqladmin/forms.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/sqladmin/forms.py b/sqladmin/forms.py index 2fa260d1..dc9d2fe1 100644 --- a/sqladmin/forms.py +++ b/sqladmin/forms.py @@ -16,7 +16,11 @@ ) import anyio -from sqlalchemy import Select, Boolean, select +from sqlalchemy import ( + Select, + Boolean, + select, +) from sqlalchemy import inspect as sqlalchemy_inspect from sqlalchemy.orm import ( ColumnProperty, From 52de7297417601a4dc7860c15b8574189cc53d2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9C=D0=B0=D0=BA=D1=81=D0=B8=D0=BC?= Date: Thu, 15 Aug 2024 10:04:03 +0300 Subject: [PATCH 4/6] Fix formatting --- sqladmin/forms.py | 161 +++++++++++----------------------------------- 1 file changed, 39 insertions(+), 122 deletions(-) diff --git a/sqladmin/forms.py b/sqladmin/forms.py index dc9d2fe1..11a67f1a 100644 --- a/sqladmin/forms.py +++ b/sqladmin/forms.py @@ -17,8 +17,8 @@ import anyio from sqlalchemy import ( - Select, Boolean, + Select, select, ) from sqlalchemy import inspect as sqlalchemy_inspect @@ -161,9 +161,7 @@ async def _prepare_kwargs( kwargs.setdefault("render_kw", widget_args) if isinstance(prop, ColumnProperty): - kwargs = self._prepare_column( - prop=prop, kwargs=kwargs, form_include_pk=form_include_pk - ) + kwargs = self._prepare_column(prop=prop, kwargs=kwargs, form_include_pk=form_include_pk) else: kwargs = await self._prepare_relationship( prop=prop, session_maker=session_maker, kwargs=kwargs, loader=loader @@ -171,9 +169,7 @@ async def _prepare_kwargs( return kwargs - def _prepare_column( - self, prop: ColumnProperty, form_include_pk: bool, kwargs: dict - ) -> Union[dict, None]: + def _prepare_column(self, prop: ColumnProperty, form_include_pk: bool, kwargs: dict) -> Union[dict, None]: assert len(prop.columns) == 1, "Multiple-column properties not supported" column = prop.columns[0] @@ -189,11 +185,7 @@ def _prepare_column( if callable_default is not None: # ColumnDefault(val).arg can be also a plain value - default = ( - callable_default(None) - if callable(callable_default) - else callable_default - ) + default = callable_default(None) if callable(callable_default) else callable_default kwargs["default"] = default optional_types = (Boolean,) @@ -226,9 +218,7 @@ async def _prepare_relationship( kwargs["allow_blank"] = nullable if not loader: - kwargs.setdefault( - "data", await self._prepare_select_options(prop, session_maker) - ) + kwargs.setdefault("data", await self._prepare_select_options(prop, session_maker)) return kwargs @@ -246,17 +236,11 @@ async def _prepare_select_options( if is_async_session_maker(session_maker): async with session_maker() as session: objects = await session.execute(stmt) - return [ - (str(self._get_identifier_value(obj)), str(obj)) - for obj in objects.scalars().unique().all() - ] + return [(str(self._get_identifier_value(obj)), str(obj)) for obj in objects.scalars().unique().all()] else: with session_maker() as session: objects = await anyio.to_thread.run_sync(session.execute, stmt) - return [ - (str(self._get_identifier_value(obj)), str(obj)) - for obj in objects.scalars().unique().all() - ] + return [(str(self._get_identifier_value(obj)), str(obj)) for obj in objects.scalars().unique().all()] def get_converter(self, prop: MODEL_PROPERTY) -> ConverterCallable: if isinstance(prop, RelationshipProperty): @@ -322,11 +306,7 @@ async def convert( assert issubclass(override, Field) return override(**kwargs) - multiple = ( - is_relationship(prop) - and prop.direction.name in ("ONETOMANY", "MANYTOMANY") - and prop.uselist - ) + multiple = is_relationship(prop) and prop.direction.name in ("ONETOMANY", "MANYTOMANY") and prop.uselist if loader: if multiple: @@ -351,27 +331,21 @@ def _string_common(prop: ColumnProperty) -> list[Validator]: return li @converts("String", "CHAR") # includes Unicode - def conv_string( - self, model: type, prop: ColumnProperty, kwargs: dict[str, Any] - ) -> UnboundField: + def conv_string(self, model: type, prop: ColumnProperty, kwargs: dict[str, Any]) -> UnboundField: extra_validators = self._string_common(prop) kwargs.setdefault("validators", []) kwargs["validators"].extend(extra_validators) return StringField(**kwargs) @converts("Text", "LargeBinary", "Binary") # includes UnicodeText - def conv_text( - self, model: type, prop: ColumnProperty, kwargs: dict[str, Any] - ) -> UnboundField: + def conv_text(self, model: type, prop: ColumnProperty, kwargs: dict[str, Any]) -> UnboundField: kwargs.setdefault("validators", []) extra_validators = self._string_common(prop) kwargs["validators"].extend(extra_validators) return TextAreaField(**kwargs) @converts("Boolean", "dialects.mssql.base.BIT") - def conv_boolean( - self, model: type, prop: ColumnProperty, kwargs: dict[str, Any] - ) -> UnboundField: + def conv_boolean(self, model: type, prop: ColumnProperty, kwargs: dict[str, Any]) -> UnboundField: if not prop.columns[0].nullable: kwargs.setdefault("render_kw", {}) kwargs["render_kw"]["class"] = "form-check-input" @@ -383,27 +357,19 @@ def conv_boolean( return SelectField(**kwargs) @converts("Date") - def conv_date( - self, model: type, prop: ColumnProperty, kwargs: dict[str, Any] - ) -> UnboundField: + def conv_date(self, model: type, prop: ColumnProperty, kwargs: dict[str, Any]) -> UnboundField: return DateField(**kwargs) @converts("Time") - def conv_time( - self, model: type, prop: ColumnProperty, kwargs: dict[str, Any] - ) -> UnboundField: + def conv_time(self, model: type, prop: ColumnProperty, kwargs: dict[str, Any]) -> UnboundField: return TimeField(**kwargs) @converts("DateTime") - def conv_datetime( - self, model: type, prop: ColumnProperty, kwargs: dict[str, Any] - ) -> UnboundField: + def conv_datetime(self, model: type, prop: ColumnProperty, kwargs: dict[str, Any]) -> UnboundField: return DateTimeField(**kwargs) @converts("Enum") - def conv_enum( - self, model: type, prop: ColumnProperty, kwargs: dict[str, Any] - ) -> UnboundField: + def conv_enum(self, model: type, prop: ColumnProperty, kwargs: dict[str, Any]) -> UnboundField: available_choices = [(e, e) for e in prop.columns[0].type.enums] accepted_values = [choice[0] for choice in available_choices] @@ -421,29 +387,21 @@ def conv_enum( return SelectField(**kwargs) @converts("Integer") # includes BigInteger and SmallInteger - def conv_integer( - self, model: type, prop: ColumnProperty, kwargs: dict[str, Any] - ) -> UnboundField: + def conv_integer(self, model: type, prop: ColumnProperty, kwargs: dict[str, Any]) -> UnboundField: return IntegerField(**kwargs) @converts("Numeric") # includes DECIMAL, Float/FLOAT, REAL, and DOUBLE - def conv_decimal( - self, model: type, prop: ColumnProperty, kwargs: dict[str, Any] - ) -> UnboundField: + def conv_decimal(self, model: type, prop: ColumnProperty, kwargs: dict[str, Any]) -> UnboundField: # override default decimal places limit, use database defaults instead kwargs.setdefault("places", None) return DecimalField(**kwargs) @converts("JSON", "JSONB") - def conv_json( - self, model: type, prop: ColumnProperty, kwargs: dict[str, Any] - ) -> UnboundField: + def conv_json(self, model: type, prop: ColumnProperty, kwargs: dict[str, Any]) -> UnboundField: return JSONField(**kwargs) @converts("Interval") - def conv_interval( - self, model: type, prop: ColumnProperty, kwargs: dict[str, Any] - ) -> UnboundField: + def conv_interval(self, model: type, prop: ColumnProperty, kwargs: dict[str, Any]) -> UnboundField: kwargs["render_kw"]["placeholder"] = "Like: 1 day 1:25:33.652" return IntervalField(**kwargs) @@ -452,9 +410,7 @@ def conv_interval( "sqlalchemy.dialects.postgresql.types.INET", "sqlalchemy_utils.types.ip_address.IPAddressType", ) - def conv_ip_address( - self, model: type, prop: ColumnProperty, kwargs: dict[str, Any] - ) -> UnboundField: + def conv_ip_address(self, model: type, prop: ColumnProperty, kwargs: dict[str, Any]) -> UnboundField: kwargs.setdefault("validators", []) kwargs["validators"].append(validators.IPAddress(ipv4=True, ipv6=True)) return StringField(**kwargs) @@ -463,9 +419,7 @@ def conv_ip_address( "sqlalchemy.dialects.postgresql.base.MACADDR", "sqlalchemy.dialects.postgresql.types.MACADDR", ) - def conv_mac_address( - self, model: type, prop: ColumnProperty, kwargs: dict[str, Any] - ) -> UnboundField: + def conv_mac_address(self, model: type, prop: ColumnProperty, kwargs: dict[str, Any]) -> UnboundField: kwargs.setdefault("validators", []) kwargs["validators"].append(validators.MacAddress()) return StringField(**kwargs) @@ -476,76 +430,54 @@ def conv_mac_address( "sqlalchemy.sql.sqltypes.Uuid", "sqlalchemy_utils.types.uuid.UUIDType", ) - def conv_uuid( - self, model: type, prop: ColumnProperty, kwargs: dict[str, Any] - ) -> UnboundField: + def conv_uuid(self, model: type, prop: ColumnProperty, kwargs: dict[str, Any]) -> UnboundField: kwargs.setdefault("validators", []) kwargs["validators"].append(validators.UUID()) return StringField(**kwargs) - @converts( - "sqlalchemy.dialects.postgresql.base.ARRAY", "sqlalchemy.sql.sqltypes.ARRAY" - ) - def conv_ARRAY( - self, model: type, prop: ColumnProperty, kwargs: dict[str, Any] - ) -> UnboundField: + @converts("sqlalchemy.dialects.postgresql.base.ARRAY", "sqlalchemy.sql.sqltypes.ARRAY") + def conv_ARRAY(self, model: type, prop: ColumnProperty, kwargs: dict[str, Any]) -> UnboundField: return Select2TagsField(**kwargs) @converts("sqlalchemy_utils.types.email.EmailType") - def conv_email( - self, model: type, prop: ColumnProperty, kwargs: dict[str, Any] - ) -> UnboundField: + def conv_email(self, model: type, prop: ColumnProperty, kwargs: dict[str, Any]) -> UnboundField: kwargs.setdefault("validators", []) kwargs["validators"].append(validators.Email()) return StringField(**kwargs) @converts("sqlalchemy_utils.types.url.URLType") - def conv_url( - self, model: type, prop: ColumnProperty, kwargs: dict[str, Any] - ) -> UnboundField: + def conv_url(self, model: type, prop: ColumnProperty, kwargs: dict[str, Any]) -> UnboundField: kwargs.setdefault("validators", []) kwargs["validators"].append(validators.URL()) return StringField(**kwargs) @converts("sqlalchemy_utils.types.currency.CurrencyType") - def conv_currency( - self, model: type, prop: ColumnProperty, kwargs: dict[str, Any] - ) -> UnboundField: + def conv_currency(self, model: type, prop: ColumnProperty, kwargs: dict[str, Any]) -> UnboundField: kwargs.setdefault("validators", []) kwargs["validators"].append(CurrencyValidator()) return StringField(**kwargs) @converts("sqlalchemy_utils.types.timezone.TimezoneType") - def conv_timezone( - self, model: type, prop: ColumnProperty, kwargs: dict[str, Any] - ) -> UnboundField: + def conv_timezone(self, model: type, prop: ColumnProperty, kwargs: dict[str, Any]) -> UnboundField: kwargs.setdefault("validators", []) - kwargs["validators"].append( - TimezoneValidator(coerce_function=prop.columns[0].type._coerce) - ) + kwargs["validators"].append(TimezoneValidator(coerce_function=prop.columns[0].type._coerce)) return StringField(**kwargs) @converts("sqlalchemy_utils.types.phone_number.PhoneNumberType") - def conv_phone_number( - self, model: type, prop: ColumnProperty, kwargs: dict[str, Any] - ) -> UnboundField: + def conv_phone_number(self, model: type, prop: ColumnProperty, kwargs: dict[str, Any]) -> UnboundField: kwargs.setdefault("validators", []) kwargs["validators"].append(PhoneNumberValidator()) return StringField(**kwargs) @converts("sqlalchemy_utils.types.color.ColorType") - def conv_color( - self, model: type, prop: ColumnProperty, kwargs: dict[str, Any] - ) -> UnboundField: + def conv_color(self, model: type, prop: ColumnProperty, kwargs: dict[str, Any]) -> UnboundField: kwargs.setdefault("validators", []) kwargs["validators"].append(ColorValidator()) return StringField(**kwargs) @converts("sqlalchemy_utils.types.choice.ChoiceType") @no_type_check - def convert_choice_type( - self, model: type, prop: ColumnProperty, kwargs: dict[str, Any] - ) -> UnboundField: + def convert_choice_type(self, model: type, prop: ColumnProperty, kwargs: dict[str, Any]) -> UnboundField: available_choices = [] column = prop.columns[0] @@ -554,10 +486,7 @@ def convert_choice_type( else: available_choices = column.type.choices - accepted_values = [ - choice[0] if isinstance(choice, tuple) else choice.value - for choice in available_choices - ] + accepted_values = [choice[0] if isinstance(choice, tuple) else choice.value for choice in available_choices] if column.nullable: kwargs["allow_blank"] = column.nullable @@ -572,34 +501,24 @@ def convert_choice_type( return SelectField(**kwargs) @converts("fastapi_storages.integrations.sqlalchemy.FileType") - def conv_file( - self, model: type, prop: ColumnProperty, kwargs: dict[str, Any] - ) -> UnboundField: + def conv_file(self, model: type, prop: ColumnProperty, kwargs: dict[str, Any]) -> UnboundField: return FileField(**kwargs) @converts("fastapi_storages.integrations.sqlalchemy.ImageType") - def conv_image( - self, model: type, prop: ColumnProperty, kwargs: dict[str, Any] - ) -> UnboundField: + def conv_image(self, model: type, prop: ColumnProperty, kwargs: dict[str, Any]) -> UnboundField: return FileField(**kwargs) @converts("ONETOONE") - def conv_one_to_one( - self, model: type, prop: RelationshipProperty, kwargs: dict[str, Any] - ) -> UnboundField: + def conv_one_to_one(self, model: type, prop: RelationshipProperty, kwargs: dict[str, Any]) -> UnboundField: kwargs["allow_blank"] = True return QuerySelectField(**kwargs) @converts("MANYTOONE") - def conv_many_to_one( - self, model: type, prop: RelationshipProperty, kwargs: dict[str, Any] - ) -> UnboundField: + def conv_many_to_one(self, model: type, prop: RelationshipProperty, kwargs: dict[str, Any]) -> UnboundField: return QuerySelectField(**kwargs) @converts("MANYTOMANY", "ONETOMANY") - def conv_many_to_many( - self, model: type, prop: RelationshipProperty, kwargs: dict[str, Any] - ) -> UnboundField: + def conv_many_to_many(self, model: type, prop: RelationshipProperty, kwargs: dict[str, Any]) -> UnboundField: return QuerySelectMultipleField(**kwargs) @@ -630,9 +549,7 @@ async def get_model_form( names = only or mapper.attrs.keys() for name in names: attr = mapper.attrs[name] - if (exclude and name in exclude) or ( - isinstance(attr, ColumnProperty) and isinstance(attr.expression, Label) - ): + if (exclude and name in exclude) or (isinstance(attr, ColumnProperty) and isinstance(attr.expression, Label)): continue attributes.append((name, attr)) From 2d11308842196732bab2e49a91a65b14679ffe33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9C=D0=B0=D0=BA=D1=81=D0=B8=D0=BC?= Date: Thu, 15 Aug 2024 09:48:04 +0300 Subject: [PATCH 5/6] Fix formatting --- sqladmin/forms.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/sqladmin/forms.py b/sqladmin/forms.py index 116b12af..dc9d2fe1 100644 --- a/sqladmin/forms.py +++ b/sqladmin/forms.py @@ -16,7 +16,11 @@ ) import anyio -from sqlalchemy import Boolean, select, Select +from sqlalchemy import ( + Select, + Boolean, + select, +) from sqlalchemy import inspect as sqlalchemy_inspect from sqlalchemy.orm import ( ColumnProperty, From f5a91fb6d0503761a5b570ea527ecff5097c0584 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9C=D0=B0=D0=BA=D1=81=D0=B8=D0=BC?= Date: Thu, 15 Aug 2024 10:04:03 +0300 Subject: [PATCH 6/6] Fix formatting --- sqladmin/forms.py | 161 +++++++++++----------------------------------- 1 file changed, 39 insertions(+), 122 deletions(-) diff --git a/sqladmin/forms.py b/sqladmin/forms.py index dc9d2fe1..11a67f1a 100644 --- a/sqladmin/forms.py +++ b/sqladmin/forms.py @@ -17,8 +17,8 @@ import anyio from sqlalchemy import ( - Select, Boolean, + Select, select, ) from sqlalchemy import inspect as sqlalchemy_inspect @@ -161,9 +161,7 @@ async def _prepare_kwargs( kwargs.setdefault("render_kw", widget_args) if isinstance(prop, ColumnProperty): - kwargs = self._prepare_column( - prop=prop, kwargs=kwargs, form_include_pk=form_include_pk - ) + kwargs = self._prepare_column(prop=prop, kwargs=kwargs, form_include_pk=form_include_pk) else: kwargs = await self._prepare_relationship( prop=prop, session_maker=session_maker, kwargs=kwargs, loader=loader @@ -171,9 +169,7 @@ async def _prepare_kwargs( return kwargs - def _prepare_column( - self, prop: ColumnProperty, form_include_pk: bool, kwargs: dict - ) -> Union[dict, None]: + def _prepare_column(self, prop: ColumnProperty, form_include_pk: bool, kwargs: dict) -> Union[dict, None]: assert len(prop.columns) == 1, "Multiple-column properties not supported" column = prop.columns[0] @@ -189,11 +185,7 @@ def _prepare_column( if callable_default is not None: # ColumnDefault(val).arg can be also a plain value - default = ( - callable_default(None) - if callable(callable_default) - else callable_default - ) + default = callable_default(None) if callable(callable_default) else callable_default kwargs["default"] = default optional_types = (Boolean,) @@ -226,9 +218,7 @@ async def _prepare_relationship( kwargs["allow_blank"] = nullable if not loader: - kwargs.setdefault( - "data", await self._prepare_select_options(prop, session_maker) - ) + kwargs.setdefault("data", await self._prepare_select_options(prop, session_maker)) return kwargs @@ -246,17 +236,11 @@ async def _prepare_select_options( if is_async_session_maker(session_maker): async with session_maker() as session: objects = await session.execute(stmt) - return [ - (str(self._get_identifier_value(obj)), str(obj)) - for obj in objects.scalars().unique().all() - ] + return [(str(self._get_identifier_value(obj)), str(obj)) for obj in objects.scalars().unique().all()] else: with session_maker() as session: objects = await anyio.to_thread.run_sync(session.execute, stmt) - return [ - (str(self._get_identifier_value(obj)), str(obj)) - for obj in objects.scalars().unique().all() - ] + return [(str(self._get_identifier_value(obj)), str(obj)) for obj in objects.scalars().unique().all()] def get_converter(self, prop: MODEL_PROPERTY) -> ConverterCallable: if isinstance(prop, RelationshipProperty): @@ -322,11 +306,7 @@ async def convert( assert issubclass(override, Field) return override(**kwargs) - multiple = ( - is_relationship(prop) - and prop.direction.name in ("ONETOMANY", "MANYTOMANY") - and prop.uselist - ) + multiple = is_relationship(prop) and prop.direction.name in ("ONETOMANY", "MANYTOMANY") and prop.uselist if loader: if multiple: @@ -351,27 +331,21 @@ def _string_common(prop: ColumnProperty) -> list[Validator]: return li @converts("String", "CHAR") # includes Unicode - def conv_string( - self, model: type, prop: ColumnProperty, kwargs: dict[str, Any] - ) -> UnboundField: + def conv_string(self, model: type, prop: ColumnProperty, kwargs: dict[str, Any]) -> UnboundField: extra_validators = self._string_common(prop) kwargs.setdefault("validators", []) kwargs["validators"].extend(extra_validators) return StringField(**kwargs) @converts("Text", "LargeBinary", "Binary") # includes UnicodeText - def conv_text( - self, model: type, prop: ColumnProperty, kwargs: dict[str, Any] - ) -> UnboundField: + def conv_text(self, model: type, prop: ColumnProperty, kwargs: dict[str, Any]) -> UnboundField: kwargs.setdefault("validators", []) extra_validators = self._string_common(prop) kwargs["validators"].extend(extra_validators) return TextAreaField(**kwargs) @converts("Boolean", "dialects.mssql.base.BIT") - def conv_boolean( - self, model: type, prop: ColumnProperty, kwargs: dict[str, Any] - ) -> UnboundField: + def conv_boolean(self, model: type, prop: ColumnProperty, kwargs: dict[str, Any]) -> UnboundField: if not prop.columns[0].nullable: kwargs.setdefault("render_kw", {}) kwargs["render_kw"]["class"] = "form-check-input" @@ -383,27 +357,19 @@ def conv_boolean( return SelectField(**kwargs) @converts("Date") - def conv_date( - self, model: type, prop: ColumnProperty, kwargs: dict[str, Any] - ) -> UnboundField: + def conv_date(self, model: type, prop: ColumnProperty, kwargs: dict[str, Any]) -> UnboundField: return DateField(**kwargs) @converts("Time") - def conv_time( - self, model: type, prop: ColumnProperty, kwargs: dict[str, Any] - ) -> UnboundField: + def conv_time(self, model: type, prop: ColumnProperty, kwargs: dict[str, Any]) -> UnboundField: return TimeField(**kwargs) @converts("DateTime") - def conv_datetime( - self, model: type, prop: ColumnProperty, kwargs: dict[str, Any] - ) -> UnboundField: + def conv_datetime(self, model: type, prop: ColumnProperty, kwargs: dict[str, Any]) -> UnboundField: return DateTimeField(**kwargs) @converts("Enum") - def conv_enum( - self, model: type, prop: ColumnProperty, kwargs: dict[str, Any] - ) -> UnboundField: + def conv_enum(self, model: type, prop: ColumnProperty, kwargs: dict[str, Any]) -> UnboundField: available_choices = [(e, e) for e in prop.columns[0].type.enums] accepted_values = [choice[0] for choice in available_choices] @@ -421,29 +387,21 @@ def conv_enum( return SelectField(**kwargs) @converts("Integer") # includes BigInteger and SmallInteger - def conv_integer( - self, model: type, prop: ColumnProperty, kwargs: dict[str, Any] - ) -> UnboundField: + def conv_integer(self, model: type, prop: ColumnProperty, kwargs: dict[str, Any]) -> UnboundField: return IntegerField(**kwargs) @converts("Numeric") # includes DECIMAL, Float/FLOAT, REAL, and DOUBLE - def conv_decimal( - self, model: type, prop: ColumnProperty, kwargs: dict[str, Any] - ) -> UnboundField: + def conv_decimal(self, model: type, prop: ColumnProperty, kwargs: dict[str, Any]) -> UnboundField: # override default decimal places limit, use database defaults instead kwargs.setdefault("places", None) return DecimalField(**kwargs) @converts("JSON", "JSONB") - def conv_json( - self, model: type, prop: ColumnProperty, kwargs: dict[str, Any] - ) -> UnboundField: + def conv_json(self, model: type, prop: ColumnProperty, kwargs: dict[str, Any]) -> UnboundField: return JSONField(**kwargs) @converts("Interval") - def conv_interval( - self, model: type, prop: ColumnProperty, kwargs: dict[str, Any] - ) -> UnboundField: + def conv_interval(self, model: type, prop: ColumnProperty, kwargs: dict[str, Any]) -> UnboundField: kwargs["render_kw"]["placeholder"] = "Like: 1 day 1:25:33.652" return IntervalField(**kwargs) @@ -452,9 +410,7 @@ def conv_interval( "sqlalchemy.dialects.postgresql.types.INET", "sqlalchemy_utils.types.ip_address.IPAddressType", ) - def conv_ip_address( - self, model: type, prop: ColumnProperty, kwargs: dict[str, Any] - ) -> UnboundField: + def conv_ip_address(self, model: type, prop: ColumnProperty, kwargs: dict[str, Any]) -> UnboundField: kwargs.setdefault("validators", []) kwargs["validators"].append(validators.IPAddress(ipv4=True, ipv6=True)) return StringField(**kwargs) @@ -463,9 +419,7 @@ def conv_ip_address( "sqlalchemy.dialects.postgresql.base.MACADDR", "sqlalchemy.dialects.postgresql.types.MACADDR", ) - def conv_mac_address( - self, model: type, prop: ColumnProperty, kwargs: dict[str, Any] - ) -> UnboundField: + def conv_mac_address(self, model: type, prop: ColumnProperty, kwargs: dict[str, Any]) -> UnboundField: kwargs.setdefault("validators", []) kwargs["validators"].append(validators.MacAddress()) return StringField(**kwargs) @@ -476,76 +430,54 @@ def conv_mac_address( "sqlalchemy.sql.sqltypes.Uuid", "sqlalchemy_utils.types.uuid.UUIDType", ) - def conv_uuid( - self, model: type, prop: ColumnProperty, kwargs: dict[str, Any] - ) -> UnboundField: + def conv_uuid(self, model: type, prop: ColumnProperty, kwargs: dict[str, Any]) -> UnboundField: kwargs.setdefault("validators", []) kwargs["validators"].append(validators.UUID()) return StringField(**kwargs) - @converts( - "sqlalchemy.dialects.postgresql.base.ARRAY", "sqlalchemy.sql.sqltypes.ARRAY" - ) - def conv_ARRAY( - self, model: type, prop: ColumnProperty, kwargs: dict[str, Any] - ) -> UnboundField: + @converts("sqlalchemy.dialects.postgresql.base.ARRAY", "sqlalchemy.sql.sqltypes.ARRAY") + def conv_ARRAY(self, model: type, prop: ColumnProperty, kwargs: dict[str, Any]) -> UnboundField: return Select2TagsField(**kwargs) @converts("sqlalchemy_utils.types.email.EmailType") - def conv_email( - self, model: type, prop: ColumnProperty, kwargs: dict[str, Any] - ) -> UnboundField: + def conv_email(self, model: type, prop: ColumnProperty, kwargs: dict[str, Any]) -> UnboundField: kwargs.setdefault("validators", []) kwargs["validators"].append(validators.Email()) return StringField(**kwargs) @converts("sqlalchemy_utils.types.url.URLType") - def conv_url( - self, model: type, prop: ColumnProperty, kwargs: dict[str, Any] - ) -> UnboundField: + def conv_url(self, model: type, prop: ColumnProperty, kwargs: dict[str, Any]) -> UnboundField: kwargs.setdefault("validators", []) kwargs["validators"].append(validators.URL()) return StringField(**kwargs) @converts("sqlalchemy_utils.types.currency.CurrencyType") - def conv_currency( - self, model: type, prop: ColumnProperty, kwargs: dict[str, Any] - ) -> UnboundField: + def conv_currency(self, model: type, prop: ColumnProperty, kwargs: dict[str, Any]) -> UnboundField: kwargs.setdefault("validators", []) kwargs["validators"].append(CurrencyValidator()) return StringField(**kwargs) @converts("sqlalchemy_utils.types.timezone.TimezoneType") - def conv_timezone( - self, model: type, prop: ColumnProperty, kwargs: dict[str, Any] - ) -> UnboundField: + def conv_timezone(self, model: type, prop: ColumnProperty, kwargs: dict[str, Any]) -> UnboundField: kwargs.setdefault("validators", []) - kwargs["validators"].append( - TimezoneValidator(coerce_function=prop.columns[0].type._coerce) - ) + kwargs["validators"].append(TimezoneValidator(coerce_function=prop.columns[0].type._coerce)) return StringField(**kwargs) @converts("sqlalchemy_utils.types.phone_number.PhoneNumberType") - def conv_phone_number( - self, model: type, prop: ColumnProperty, kwargs: dict[str, Any] - ) -> UnboundField: + def conv_phone_number(self, model: type, prop: ColumnProperty, kwargs: dict[str, Any]) -> UnboundField: kwargs.setdefault("validators", []) kwargs["validators"].append(PhoneNumberValidator()) return StringField(**kwargs) @converts("sqlalchemy_utils.types.color.ColorType") - def conv_color( - self, model: type, prop: ColumnProperty, kwargs: dict[str, Any] - ) -> UnboundField: + def conv_color(self, model: type, prop: ColumnProperty, kwargs: dict[str, Any]) -> UnboundField: kwargs.setdefault("validators", []) kwargs["validators"].append(ColorValidator()) return StringField(**kwargs) @converts("sqlalchemy_utils.types.choice.ChoiceType") @no_type_check - def convert_choice_type( - self, model: type, prop: ColumnProperty, kwargs: dict[str, Any] - ) -> UnboundField: + def convert_choice_type(self, model: type, prop: ColumnProperty, kwargs: dict[str, Any]) -> UnboundField: available_choices = [] column = prop.columns[0] @@ -554,10 +486,7 @@ def convert_choice_type( else: available_choices = column.type.choices - accepted_values = [ - choice[0] if isinstance(choice, tuple) else choice.value - for choice in available_choices - ] + accepted_values = [choice[0] if isinstance(choice, tuple) else choice.value for choice in available_choices] if column.nullable: kwargs["allow_blank"] = column.nullable @@ -572,34 +501,24 @@ def convert_choice_type( return SelectField(**kwargs) @converts("fastapi_storages.integrations.sqlalchemy.FileType") - def conv_file( - self, model: type, prop: ColumnProperty, kwargs: dict[str, Any] - ) -> UnboundField: + def conv_file(self, model: type, prop: ColumnProperty, kwargs: dict[str, Any]) -> UnboundField: return FileField(**kwargs) @converts("fastapi_storages.integrations.sqlalchemy.ImageType") - def conv_image( - self, model: type, prop: ColumnProperty, kwargs: dict[str, Any] - ) -> UnboundField: + def conv_image(self, model: type, prop: ColumnProperty, kwargs: dict[str, Any]) -> UnboundField: return FileField(**kwargs) @converts("ONETOONE") - def conv_one_to_one( - self, model: type, prop: RelationshipProperty, kwargs: dict[str, Any] - ) -> UnboundField: + def conv_one_to_one(self, model: type, prop: RelationshipProperty, kwargs: dict[str, Any]) -> UnboundField: kwargs["allow_blank"] = True return QuerySelectField(**kwargs) @converts("MANYTOONE") - def conv_many_to_one( - self, model: type, prop: RelationshipProperty, kwargs: dict[str, Any] - ) -> UnboundField: + def conv_many_to_one(self, model: type, prop: RelationshipProperty, kwargs: dict[str, Any]) -> UnboundField: return QuerySelectField(**kwargs) @converts("MANYTOMANY", "ONETOMANY") - def conv_many_to_many( - self, model: type, prop: RelationshipProperty, kwargs: dict[str, Any] - ) -> UnboundField: + def conv_many_to_many(self, model: type, prop: RelationshipProperty, kwargs: dict[str, Any]) -> UnboundField: return QuerySelectMultipleField(**kwargs) @@ -630,9 +549,7 @@ async def get_model_form( names = only or mapper.attrs.keys() for name in names: attr = mapper.attrs[name] - if (exclude and name in exclude) or ( - isinstance(attr, ColumnProperty) and isinstance(attr.expression, Label) - ): + if (exclude and name in exclude) or (isinstance(attr, ColumnProperty) and isinstance(attr.expression, Label)): continue attributes.append((name, attr))