aminalaee / sqladmin

SQLAlchemy Admin for FastAPI and Starlette
https://aminalaee.dev/sqladmin/
BSD 3-Clause "New" or "Revised" License
1.81k stars 182 forks source link

ImageType support issue #470

Closed graky closed 1 year ago

graky commented 1 year ago

Checklist

Describe the bug

According to the documentation, sqladmin supports ImageType. However, I encountered an issue when using it in my models.

My models are defined as follows:

class MessageToSend(Base):
    __tablename__ = "message_to_send"
    id = Column(sqlalchemy.Integer, primary_key=True, index=True)
    message_text = Column(sqlalchemy.Text, nullable=True)
    datetime_to_send = Column(sqlalchemy.DateTime, nullable=True)
    instant_send = Column(sqlalchemy.Boolean, default=False)
    images = relationship(
        "ImagesToSend",
        cascade="all,delete",
        back_populates="message",
    )
    files = relationship(
        "FileToSend",
        cascade="all,delete",
        back_populates="message",
    )

    def __repr__(self):
        return f"{self.id}, {self.message_text[:30]}"

class ImagesToSend(Base):
    __tablename__ = "image_to_send"
    id = Column(sqlalchemy.Integer, primary_key=True, index=True)
    image = Column(ImageType(storage=FileSystemStorage(path="media/images/")))
    message_id = Column(
        sqlalchemy.Integer,
        ForeignKey(MessageToSend.id, ondelete="CASCADE"),
    )
    message = relationship("MessageToSend", foreign_keys="ImagesToSend.message_id")

class FileToSend(Base):
    __tablename__ = "file_to_send"
    id = Column(sqlalchemy.Integer, primary_key=True, index=True)
    file = Column(FileType(storage=FileSystemStorage(path="media/files/")))
    message_id = Column(
        sqlalchemy.Integer,
        ForeignKey(MessageToSend.id, ondelete="CASCADE"),
    )
    message = relationship("MessageToSend", foreign_keys="FileToSend.message_id")

And the corresponding ModelView classes:

class MessageToSendAdmin(ModelView, model=MessageToSend):
    column_list = [MessageToSend.id, MessageToSend.datetime_to_send]

class ImagesAdmin(ModelView, model=ImagesToSend):
    column_list = [ImagesToSend.id, ImagesToSend.message_id]

class FilesAdmin(ModelView, model=FileToSend):
    column_list = [FileToSend.id, FileToSend.message_id]

While FileType works as expected, I encounter the following error when accessing the ImagesToSend edit page:

sqladmin.exceptions.NoConverterFound: Could not find field converter for column image (<class 'sqlalchemy_fields.types.image.ImageType'>).

I have reviewed the source code but could not find a suitable converter. Is this an issue with my implementation or a misunderstanding of the documentation? Any guidance or clarification would be appreciated

Steps to reproduce the bug

No response

Expected behavior

No response

Actual behavior

No response

Debugging material

Full traceback:

INFO: 127.0.0.1:56373 - "GET /images-to-send/create HTTP/1.1" 500 Internal Server Error ERROR: Exception in ASGI application Traceback (most recent call last): File "C:\Users\akimg.virtualenvs\projects-UdMq79XO\lib\site-packages\uvicorn\protocols\http\httptools_impl.py", line 436, in run_asgi result = await app( # type: ignore[func-returns-value] File "C:\Users\akimg.virtualenvs\projects-UdMq79XO\lib\site-packages\uvicorn\middleware\proxy_headers.py", line 78, in call return await self.app(scope, receive, send) File "C:\Users\akimg.virtualenvs\projects-UdMq79XO\lib\site-packages\fastapi\applications.py", line 276, in call await super().call(scope, receive, send) File "C:\Users\akimg.virtualenvs\projects-UdMq79XO\lib\site-packages\starlette\applications.py", line 122, in call await self.middleware_stack(scope, receive, send) File "C:\Users\akimg.virtualenvs\projects-UdMq79XO\lib\site-packages\starlette\middleware\errors.py", line 184, in call raise exc File "C:\Users\akimg.virtualenvs\projects-UdMq79XO\lib\site-packages\starlette\middleware\errors.py", line 162, in call await self.app(scope, receive, _send) File "C:\Users\akimg.virtualenvs\projects-UdMq79XO\lib\site-packages\starlette\middleware\cors.py", line 84, in call await self.app(scope, receive, send) File "C:\Users\akimg.virtualenvs\projects-UdMq79XO\lib\site-packages\starlette\middleware\base.py", line 109, in call await response(scope, receive, send) File "C:\Users\akimg.virtualenvs\projects-UdMq79XO\lib\site-packages\starlette\responses.py", line 270, in call async with anyio.create_task_group() as task_group: File "C:\Users\akimg.virtualenvs\projects-UdMq79XO\lib\site-packages\anyio_backends_asyncio.py", line 662, in aexit raise exceptions[0] File "C:\Users\akimg.virtualenvs\projects-UdMq79XO\lib\site-packages\starlette\responses.py", line 273, in wrap await func() File "C:\Users\akimg.virtualenvs\projects-UdMq79XO\lib\site-packages\starlette\middleware\base.py", line 134, in stream_response return await super().stream_response(send) File "C:\Users\akimg.virtualenvs\projects-UdMq79XO\lib\site-packages\starlette\responses.py", line 262, in stream_response async for chunk in self.body_iterator: File "C:\Users\akimg.virtualenvs\projects-UdMq79XO\lib\site-packages\starlette\middleware\base.py", line 98, in body_stream raise app_exc File "C:\Users\akimg.virtualenvs\projects-UdMq79XO\lib\site-packages\starlette\middleware\base.py", line 70, in coro await self.app(scope, receive_or_disconnect, send_no_error) File "C:\Users\akimg.virtualenvs\projects-UdMq79XO\lib\site-packages\starlette\middleware\exceptions.py", line 79, in call raise exc File "C:\Users\akimg.virtualenvs\projects-UdMq79XO\lib\site-packages\starlette\middleware\exceptions.py", line 68, in call await self.app(scope, receive, sender) File "C:\Users\akimg.virtualenvs\projects-UdMq79XO\lib\site-packages\fastapi\middleware\asyncexitstack.py", line 21, in call raise e File "C:\Users\akimg.virtualenvs\projects-UdMq79XO\lib\site-packages\fastapi\middleware\asyncexitstack.py", line 18, in call await self.app(scope, receive, send) File "C:\Users\akimg.virtualenvs\projects-UdMq79XO\lib\site-packages\starlette\routing.py", line 718, in call await route.handle(scope, receive, send) File "C:\Users\akimg.virtualenvs\projects-UdMq79XO\lib\site-packages\starlette\routing.py", line 443, in handle await self.app(scope, receive, send) File "C:\Users\akimg.virtualenvs\projects-UdMq79XO\lib\site-packages\starlette\applications.py", line 122, in call await self.middleware_stack(scope, receive, send) File "C:\Users\akimg.virtualenvs\projects-UdMq79XO\lib\site-packages\starlette\middleware\errors.py", line 184, in call raise exc File "C:\Users\akimg.virtualenvs\projects-UdMq79XO\lib\site-packages\starlette\middleware\errors.py", line 162, in call await self.app(scope, receive, _send) File "C:\Users\akimg.virtualenvs\projects-UdMq79XO\lib\site-packages\starlette\middleware\sessions.py", line 86, in call await self.app(scope, receive, send_wrapper) File "C:\Users\akimg.virtualenvs\projects-UdMq79XO\lib\site-packages\starlette\middleware\exceptions.py", line 79, in call raise exc File "C:\Users\akimg.virtualenvs\projects-UdMq79XO\lib\site-packages\starlette\middleware\exceptions.py", line 68, in call await self.app(scope, receive, sender) File "C:\Users\akimg.virtualenvs\projects-UdMq79XO\lib\site-packages\starlette\routing.py", line 718, in call await route.handle(scope, receive, send) File "C:\Users\akimg.virtualenvs\projects-UdMq79XO\lib\site-packages\starlette\routing.py", line 276, in handle await self.app(scope, receive, send) File "C:\Users\akimg.virtualenvs\projects-UdMq79XO\lib\site-packages\starlette\routing.py", line 66, in app response = await func(request) File "C:\Users\akimg.virtualenvs\projects-UdMq79XO\lib\site-packages\sqladmin\authentication.py", line 60, in wrapper_decorator return await func(*args, **kwargs) File "C:\Users\akimg.virtualenvs\projects-UdMq79XO\lib\site-packages\sqladmin\application.py", line 429, in create Form = await model_view.scaffold_form() File "C:\Users\akimg.virtualenvs\projects-UdMq79XO\lib\site-packages\sqladmin\models.py", line 1004, in scaffold_form return await get_model_form( File "C:\Users\akimg.virtualenvs\projects-UdMq79XO\lib\site-packages\sqladmin\forms.py", line 580, in get_model_form field = await converter.convert( File "C:\Users\akimg.virtualenvs\projects-UdMq79XO\lib\site-packages\sqladmin\forms.py", line 312, in convert converter = self.get_converter(prop=prop) File "C:\Users\akimg.virtualenvs\projects-UdMq79XO\lib\site-packages\sqladmin\forms.py", line 266, in get_converter raise NoConverterFound( # pragma: nocover sqladmin.exceptions.NoConverterFound: Could not find field converter for column image (<class 'sqlalchemy_fields.types.image.ImageType'>).

Environment

win11/Python3.10/sqladmin==0.10.2

Pipenv graph:

alembic==1.10.3

Additional context

No response

aminalaee commented 1 year ago

Hi, you are correct. This is a missing configuration, I can add it.

graky commented 1 year ago

Thanks a lot!