Home > Software engineering >  NoConverterFound error in SQLAdmin while working with PostgreSQL ARRAY type
NoConverterFound error in SQLAdmin while working with PostgreSQL ARRAY type

Time:06-05

I am using the SQLAdmin python package with SQLModel library in the FastAPI app to create an admin dashboard for a model that has an array field and experience the following error:

 ERROR:    Exception in ASGI application
2022-06-03T19:03:11.662449 00:00 app[web.1]: Traceback (most recent call last):
2022-06-03T19:03:11.662449 00:00 app[web.1]: File "/app/.heroku/python/lib/python3.8/site-packages/uvicorn/protocols/http/httptools_impl.py", line 372, in run_asgi
2022-06-03T19:03:11.662450 00:00 app[web.1]: result = await app(self.scope, self.receive, self.send)
2022-06-03T19:03:11.662450 00:00 app[web.1]: File "/app/.heroku/python/lib/python3.8/site-packages/uvicorn/middleware/proxy_headers.py", line 75, in __call__
2022-06-03T19:03:11.662451 00:00 app[web.1]: return await self.app(scope, receive, send)
2022-06-03T19:03:11.662457 00:00 app[web.1]: File "/app/.heroku/python/lib/python3.8/site-packages/fastapi/applications.py", line 261, in __call__
2022-06-03T19:03:11.662457 00:00 app[web.1]: await super().__call__(scope, receive, send)
2022-06-03T19:03:11.662458 00:00 app[web.1]: File "/app/.heroku/python/lib/python3.8/site-packages/starlette/applications.py", line 112, in __call__
2022-06-03T19:03:11.662458 00:00 app[web.1]: await self.middleware_stack(scope, receive, send)
2022-06-03T19:03:11.662458 00:00 app[web.1]: File "/app/.heroku/python/lib/python3.8/site-packages/starlette/middleware/errors.py", line 181, in __call__
2022-06-03T19:03:11.662459 00:00 app[web.1]: raise exc
2022-06-03T19:03:11.662459 00:00 app[web.1]: File "/app/.heroku/python/lib/python3.8/site-packages/starlette/middleware/errors.py", line 159, in __call__
2022-06-03T19:03:11.662460 00:00 app[web.1]: await self.app(scope, receive, _send)
2022-06-03T19:03:11.662460 00:00 app[web.1]: File "/app/.heroku/python/lib/python3.8/site-packages/starlette/middleware/cors.py", line 84, in __call__
2022-06-03T19:03:11.662461 00:00 app[web.1]: await self.app(scope, receive, send)
2022-06-03T19:03:11.662461 00:00 app[web.1]: File "/app/.heroku/python/lib/python3.8/site-packages/starlette/exceptions.py", line 82, in __call__
2022-06-03T19:03:11.662462 00:00 app[web.1]: raise exc
2022-06-03T19:03:11.662462 00:00 app[web.1]: File "/app/.heroku/python/lib/python3.8/site-packages/starlette/exceptions.py", line 71, in __call__
2022-06-03T19:03:11.662462 00:00 app[web.1]: await self.app(scope, receive, sender)
2022-06-03T19:03:11.662463 00:00 app[web.1]: File "/app/.heroku/python/lib/python3.8/site-packages/fastapi/middleware/asyncexitstack.py", line 21, in __call__
2022-06-03T19:03:11.662463 00:00 app[web.1]: raise e
2022-06-03T19:03:11.662463 00:00 app[web.1]: File "/app/.heroku/python/lib/python3.8/site-packages/fastapi/middleware/asyncexitstack.py", line 18, in __call__
2022-06-03T19:03:11.662463 00:00 app[web.1]: await self.app(scope, receive, send)
2022-06-03T19:03:11.662464 00:00 app[web.1]: File "/app/.heroku/python/lib/python3.8/site-packages/starlette/routing.py", line 656, in __call__
2022-06-03T19:03:11.662464 00:00 app[web.1]: await route.handle(scope, receive, send)
2022-06-03T19:03:11.662464 00:00 app[web.1]: File "/app/.heroku/python/lib/python3.8/site-packages/starlette/routing.py", line 408, in handle
2022-06-03T19:03:11.662465 00:00 app[web.1]: await self.app(scope, receive, send)
2022-06-03T19:03:11.662465 00:00 app[web.1]: File "/app/.heroku/python/lib/python3.8/site-packages/starlette/applications.py", line 112, in __call__
2022-06-03T19:03:11.662465 00:00 app[web.1]: await self.middleware_stack(scope, receive, send)
2022-06-03T19:03:11.662465 00:00 app[web.1]: File "/app/.heroku/python/lib/python3.8/site-packages/starlette/middleware/errors.py", line 181, in __call__
2022-06-03T19:03:11.662466 00:00 app[web.1]: raise exc
2022-06-03T19:03:11.662466 00:00 app[web.1]: File "/app/.heroku/python/lib/python3.8/site-packages/starlette/middleware/errors.py", line 159, in __call__
2022-06-03T19:03:11.662466 00:00 app[web.1]: await self.app(scope, receive, _send)
2022-06-03T19:03:11.662466 00:00 app[web.1]: File "/app/.heroku/python/lib/python3.8/site-packages/starlette/exceptions.py", line 82, in __call__
2022-06-03T19:03:11.662466 00:00 app[web.1]: raise exc
2022-06-03T19:03:11.662467 00:00 app[web.1]: File "/app/.heroku/python/lib/python3.8/site-packages/starlette/exceptions.py", line 71, in __call__
2022-06-03T19:03:11.662467 00:00 app[web.1]: await self.app(scope, receive, sender)
2022-06-03T19:03:11.662467 00:00 app[web.1]: File "/app/.heroku/python/lib/python3.8/site-packages/starlette/routing.py", line 656, in __call__
2022-06-03T19:03:11.662467 00:00 app[web.1]: await route.handle(scope, receive, send)
2022-06-03T19:03:11.662468 00:00 app[web.1]: File "/app/.heroku/python/lib/python3.8/site-packages/starlette/routing.py", line 259, in handle
2022-06-03T19:03:11.662468 00:00 app[web.1]: await self.app(scope, receive, send)
2022-06-03T19:03:11.662468 00:00 app[web.1]: File "/app/.heroku/python/lib/python3.8/site-packages/starlette/routing.py", line 61, in app
2022-06-03T19:03:11.662469 00:00 app[web.1]: response = await func(request)
2022-06-03T19:03:11.662469 00:00 app[web.1]: File "/app/.heroku/python/lib/python3.8/site-packages/sqladmin/application.py", line 351, in edit
2022-06-03T19:03:11.662471 00:00 app[web.1]: Form = await model_admin.scaffold_form()
2022-06-03T19:03:11.662471 00:00 app[web.1]: File "/app/.heroku/python/lib/python3.8/site-packages/sqladmin/models.py", line 834, in scaffold_form
2022-06-03T19:03:11.662471 00:00 app[web.1]: return await get_model_form(
2022-06-03T19:03:11.662472 00:00 app[web.1]: File "/app/.heroku/python/lib/python3.8/site-packages/sqladmin/forms.py", line 415, in get_model_form
2022-06-03T19:03:11.662472 00:00 app[web.1]: field = await converter.convert(
2022-06-03T19:03:11.662472 00:00 app[web.1]: File "/app/.heroku/python/lib/python3.8/site-packages/sqladmin/forms.py", line 232, in convert
2022-06-03T19:03:11.662472 00:00 app[web.1]: converter = self.get_converter(model=model, prop=prop)
2022-06-03T19:03:11.662473 00:00 app[web.1]: File "/app/.heroku/python/lib/python3.8/site-packages/sqladmin/forms.py", line 116, in get_converter
2022-06-03T19:03:11.662473 00:00 app[web.1]: raise NoConverterFound(  # pragma: nocover
2022-06-03T19:03:11.662474 00:00 app[web.1]: sqladmin.exceptions.NoConverterFound: Could not find field converter for column series (<class 'sqlalchemy.sql.sqltypes.ARRAY'>).

This error is thrown when I am trying to edit the following entity:

class ShowCreate(SQLModel):
    title: str
    description: str
    language: Language = Field(sa_column=Column(Enum(Language)))
    show_copyright: str
    category: Category = Field(sa_column=Column(Enum(Category)))
    series: Set[str] = Field(default=None, sa_column=Column(ARRAY(String())))

I understand that the problem is caused by the series field, but I really need to store a list of strings in PostgreSQL and have the ability to modify the Show entity from the SQLAdmin dashboard. Is there any way to explain SQLAdmin how to handle array type?

CodePudding user response:

I am not able to reproduce your issue in SQLAlchemy only, which I expected since the traceback seems to be an issue with getting the converter for <class 'sqlalchemy.sql.sqltypes.ARRAY'> within SQLAdmin.

This is the generic ARRAY type, you could try to use the postgresql.ARRAY type directly.

If that still does not work, you should file an issue with SQLAdmin, the authors might be able to help you better.

PS: storing an array in a row violates the first normal form, you might want to think about that.

CodePudding user response:

Thank you, I did additonal investigation and found out that sqladmin supports following convertors:

{
    'dialects.mssql.base.BIT': "<bound method ModelConverter.conv_Boolean of <sqladmin.forms.ModelConverter object at 0x10637fb80>>",
    'Boolean': "<bound method ModelConverter.conv_Boolean of <sqladmin.forms.ModelConverter object at 0x10637fb80>>",
    'Date': "<bound method ModelConverter.conv_Date of <sqladmin.forms.ModelConverter object at 0x10637fb80>>",
    'DateTime': "<bound method ModelConverter.conv_DateTime of <sqladmin.forms.ModelConverter object at 0x10637fb80>>",
    'Enum': "<bound method ModelConverter.conv_Enum of <sqladmin.forms.ModelConverter object at 0x10637fb80>>",
    'MANYTOMANY': "<bound method ModelConverter.conv_ManyToMany of <sqladmin.forms.ModelConverter object at 0x10637fb80>>",
    'ONETOMANY': "<bound method ModelConverter.conv_ManyToMany of <sqladmin.forms.ModelConverter object at 0x10637fb80>>",
    'MANYTOONE': "<bound method ModelConverter.conv_ManyToOne of <sqladmin.forms.ModelConverter object at 0x10637fb80>>",
    'sqlalchemy.dialects.postgresql.base.INET': "<bound method ModelConverter.conv_PGInet of <sqladmin.forms.ModelConverter object at 0x10637fb80>>",
    'sqlalchemy.dialects.postgresql.base.MACADDR': "<bound method ModelConverter.conv_PGMacaddr of <sqladmin.forms.ModelConverter object at 0x10637fb80>>",
    'sqlalchemy.dialects.postgresql.base.UUID': "<bound method ModelConverter.conv_PgUuid of <sqladmin.forms.ModelConverter object at 0x10637fb80>>",
    'CHAR': "<bound method ModelConverter.conv_String of <sqladmin.forms.ModelConverter object at 0x10637fb80>>",
    'String': "<bound method ModelConverter.conv_String of <sqladmin.forms.ModelConverter object at 0x10637fb80>>",
    'LargeBinary': "<bound method ModelConverter.conv_Text of <sqladmin.forms.ModelConverter object at 0x10637fb80>>",
    'Text': "<bound method ModelConverter.conv_Text of <sqladmin.forms.ModelConverter object at 0x10637fb80>>",
    'Binary': "<bound method ModelConverter.conv_Text of <sqladmin.forms.ModelConverter object at 0x10637fb80>>",
    'sqlalchemy_utils.types.email.EmailType': "<bound method ModelConverter.conv_UtilsEmail of <sqladmin.forms.ModelConverter object at 0x10637fb80>>",
    'sqlalchemy_utils.types.ip_address.IPAddressType': "<bound method ModelConverter.conv_UtilsIP of <sqladmin.forms.ModelConverter object at 0x10637fb80>>",
    'JSON': "<bound method ModelConverter.convert_JSON of <sqladmin.forms.ModelConverter object at 0x10637fb80>>",
    'Numeric': "<bound method ModelConverter.handle_decimal_types of <sqladmin.forms.ModelConverter object at 0x10637fb80>>",
    'Integer': "<bound method ModelConverter.handle_integer_types of <sqladmin.forms.ModelConverter object at 0x10637fb80>>"
}

So, there is OneToMany support available, but no ARRAY.

The solution I took for my system is to create a new Table and use OneToMany.

  • Related