Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix ModelConverter when impl is not callable #186

Merged
merged 1 commit into from
Jun 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 10 additions & 6 deletions sqladmin/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ def __init__(self) -> None:
super().__init__()
self._register_converters()

def _register_converters(self):
def _register_converters(self) -> None:
converters = {}

for name in dir(self):
Expand All @@ -94,13 +94,12 @@ async def _prepare_kwargs(
engine: Union[Engine, AsyncEngine],
field_args: Dict[str, Any] = None,
label: Optional[str] = None,
) -> Dict[str, Any]:
) -> Optional[Dict[str, Any]]:
if field_args:
kwargs = field_args.copy()
else:
kwargs = {}

kwargs: Dict[str, Any]
kwargs.setdefault("label", label)
kwargs.setdefault("validators", [])
kwargs.setdefault("filters", [])
Expand All @@ -115,7 +114,7 @@ async def _prepare_kwargs(
column = prop.columns[0]

if column.primary_key or column.foreign_keys:
return
return None

default = getattr(column, "default", None)

Expand Down Expand Up @@ -198,8 +197,13 @@ def get_converter(

# Support for custom types like SQLModel which inherit TypeDecorator
if hasattr(col_type, "impl"):
if col_type.impl.__name__ in self._converters: # type: ignore
return self._converters[col_type.impl.__name__] # type: ignore
if callable(col_type.impl): # type: ignore
impl = col_type.impl # type: ignore
else:
impl = col_type.impl.__class__ # type: ignore

if impl.__name__ in self._converters:
return self._converters[impl.__name__]

raise NoConverterFound( # pragma: nocover
f"Could not find field converter for column {column.name} ({types[0]!r})."
Expand Down
29 changes: 29 additions & 0 deletions tests/test_forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
Numeric,
String,
Text,
TypeDecorator,
)
from sqlalchemy.dialects.postgresql import INET, MACADDR, UUID
from sqlalchemy.ext.asyncio import AsyncSession
Expand Down Expand Up @@ -178,3 +179,31 @@ class UserAdmin(ModelAdmin, model=User):
assert isinstance(form, MyForm)
assert len(form._fields) == 1
assert "foo" in form._fields


async def test_form_converter_when_impl_is_callable() -> None:
class MyType(TypeDecorator):
impl = String

class CustomModel(Base):
__tablename__ = "impl_callable"

id = Column(Integer, primary_key=True)
custom = Column(MyType)

Form = await get_model_form(model=CustomModel, engine=engine)
assert "custom" in Form()._fields


async def test_form_converter_when_impl_not_callable() -> None:
class MyType(TypeDecorator):
impl = String(length=100)

class CustomModel(Base):
__tablename__ = "impl_non_callable"

id = Column(Integer, primary_key=True)
custom = Column(MyType)

Form = await get_model_form(model=CustomModel, engine=engine)
assert "custom" in Form()._fields