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

Bug: composite primary key from 2 columns, 1 being a URI, causes exception in 0.13.1 - AssertionError: May not contain path separators #488

Open
sglebs opened this issue Jan 23, 2024 · 1 comment
Labels
bug Something isn't working

Comments

@sglebs
Copy link

sglebs commented Jan 23, 2024

Describe the bug

I have this entity:

class KnowledgeSource(Base):
    __tablename__ = 'knowledge_sources'
    __table_args__ = (PrimaryKeyConstraint('knowledge_ingestion_profile_id', 'uri'),
                      )
    knowledge_ingestion_profile_id = Column(ForeignKey('knowledge_ingestion_profiles.id', ondelete='SET NULL', onupdate='CASCADE'))
    knowledge_ingestion_profile = relationship('KnowledgeIngestionProfile')
    uri = Column(Text)
    status = Column(Enum(KnowledgeSourceState, name='KnowledgeSourceState'), server_default=text("'PENDING'::\"KnowledgeSourceState\""))
    # do we need an ID for it after being incorporated? Or is the URI the ID itself?
    # scenario: The user excludes it, and we need to exclude all the chunks in the vector database
    updated_at = Column(TIMESTAMP(timezone=True, precision=3), nullable=False, server_default=text("CURRENT_TIMESTAMP"))

In 0.11.2 I cannot show it in the admin UI, it gives me an error on startup:

Traceback (most recent call last):
  File "...app/main.py", line 46, in <module>
    create_admin(engine, base_url="/").mount_to(app)
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "...app/admin/adminviews.py", line 406, in create_admin
    admin.add_view(KnowledgeSourceAdminView(model=KnowledgeSource, icon="fa fa-file"))
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "...venv/lib/python3.11/site-packages/starlette_admin/contrib/sqla/view.py", line 54, in __init__
    assert len(mapper.primary_key) == 1, (
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: Multiple PK columns not supported, A possible solution is to override BaseModelView class and put your own logic 

With 0.13.1 I get something different:

  File ".../venv/lib/python3.11/site-packages/starlette/convertors.py", line 26, in to_string
    assert "/" not in value, "May not contain path separators"
           ^^^^^^^^^^^^^^^^

and it comes from here:

 File ".../venv/lib/python3.11/site-packages/starlette_admin/views.py", line 760, in serialize
    request.url_for(route_name + ":detail", identity=self.identity, pk=pk)

This is the source:

    def to_string(self, value: str) -> str:
        value = str(value)
        assert "/" not in value, "May not contain path separators"
        assert value, "Must not be empty"
        return value

NOTE: obviously my entity has a slot/column named "uri" and values ALWAYS do contain "/".

To Reproduce

Have an entity like mine, above. Insert entities via code and try to look at them in the admin. It will cause the error in the console. I suspect you need a string slot and insert something with an "/" in it.

Environment (please complete the following information):

  • Starlette-Admin version: 0.13.1
  • ORM/ODMs: SQLAlchemy, psycopg2-binary==2.9.9
  • SQLAlchemy-serializer==1.4.1

Additional context

I have another regular entity, simple primary key, and it does have uri slot/column. No indues with it. I suspect the problem is related to dual-column PKs. The value of the columns cannot have "/" in the current implementation.

Full stack trace:

Traceback (most recent call last):
  File "...venv/lib/python3.11/site-packages/uvicorn/protocols/http/httptools_impl.py", line 419, in run_asgi
    result = await app(  # type: ignore[func-returns-value]
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "...venv/lib/python3.11/site-packages/uvicorn/middleware/proxy_headers.py", line 78, in __call__
    return await self.app(scope, receive, send)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "...venv/lib/python3.11/site-packages/fastapi/applications.py", line 284, in __call__
    await super().__call__(scope, receive, send)
  File "...venv/lib/python3.11/site-packages/starlette/applications.py", line 122, in __call__
    await self.middleware_stack(scope, receive, send)
  File "...venv/lib/python3.11/site-packages/starlette/middleware/errors.py", line 184, in __call__
    raise exc
  File "...venv/lib/python3.11/site-packages/starlette/middleware/errors.py", line 162, in __call__
    await self.app(scope, receive, _send)
  File "...venv/lib/python3.11/site-packages/starlette/middleware/sessions.py", line 86, in __call__
    await self.app(scope, receive, send_wrapper)
  File "...venv/lib/python3.11/site-packages/starlette/middleware/sessions.py", line 86, in __call__
    await self.app(scope, receive, send_wrapper)
  File "...venv/lib/python3.11/site-packages/starlette/middleware/exceptions.py", line 79, in __call__
    raise exc
  File "...venv/lib/python3.11/site-packages/starlette/middleware/exceptions.py", line 68, in __call__
    await self.app(scope, receive, sender)
  File "...venv/lib/python3.11/site-packages/fastapi/middleware/asyncexitstack.py", line 20, in __call__
    raise e
  File "...venv/lib/python3.11/site-packages/fastapi/middleware/asyncexitstack.py", line 17, in __call__
    await self.app(scope, receive, send)
  File "...venv/lib/python3.11/site-packages/starlette/routing.py", line 718, in __call__
    await route.handle(scope, receive, send)
  File "...venv/lib/python3.11/site-packages/starlette/routing.py", line 443, in handle
    await self.app(scope, receive, send)
  File "...venv/lib/python3.11/site-packages/starlette/applications.py", line 122, in __call__
    await self.middleware_stack(scope, receive, send)
  File "...venv/lib/python3.11/site-packages/starlette/middleware/errors.py", line 184, in __call__
    raise exc
  File "...venv/lib/python3.11/site-packages/starlette/middleware/errors.py", line 162, in __call__
    await self.app(scope, receive, _send)
  File "...venv/lib/python3.11/site-packages/starlette/middleware/base.py", line 108, in __call__
    response = await self.dispatch_func(request, call_next)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "...venv/lib/python3.11/site-packages/starlette_admin/contrib/sqla/middleware.py", line 28, in dispatch
    return await call_next(request)
           ^^^^^^^^^^^^^^^^^^^^^^^^
  File "...venv/lib/python3.11/site-packages/starlette/middleware/base.py", line 84, in call_next
    raise app_exc
  File "...venv/lib/python3.11/site-packages/starlette/middleware/base.py", line 70, in coro
    await self.app(scope, receive_or_disconnect, send_no_error)
  File "...venv/lib/python3.11/site-packages/starlette_admin/i18n.py", line 162, in __call__
    await self.app(scope, receive, send)
  File "...venv/lib/python3.11/site-packages/starlette/middleware/sessions.py", line 86, in __call__
    await self.app(scope, receive, send_wrapper)
  File "...venv/lib/python3.11/site-packages/starlette/middleware/base.py", line 108, in __call__
    response = await self.dispatch_func(request, call_next)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "...venv/lib/python3.11/site-packages/starlette_admin/auth.py", line 360, in dispatch
    return await call_next(request)
           ^^^^^^^^^^^^^^^^^^^^^^^^
  File "...venv/lib/python3.11/site-packages/starlette/middleware/base.py", line 84, in call_next
    raise app_exc
  File "...venv/lib/python3.11/site-packages/starlette/middleware/base.py", line 70, in coro
    await self.app(scope, receive_or_disconnect, send_no_error)
  File "...venv/lib/python3.11/site-packages/starlette/middleware/exceptions.py", line 79, in __call__
    raise exc
  File "...venv/lib/python3.11/site-packages/starlette/middleware/exceptions.py", line 68, in __call__
    await self.app(scope, receive, sender)
  File "...venv/lib/python3.11/site-packages/starlette/routing.py", line 718, in __call__
    await route.handle(scope, receive, send)
  File "...venv/lib/python3.11/site-packages/starlette/routing.py", line 276, in handle
    await self.app(scope, receive, send)
  File "...venv/lib/python3.11/site-packages/starlette/routing.py", line 66, in app
    response = await func(request)
               ^^^^^^^^^^^^^^^^^^^
  File "...venv/lib/python3.11/site-packages/starlette_admin/base.py", line 299, in _render_api
    serialized_items = [
                       ^
  File "...venv/lib/python3.11/site-packages/starlette_admin/base.py", line 301, in <listcomp>
    await model.serialize(
  File "...venv/lib/python3.11/site-packages/starlette_admin/views.py", line 760, in serialize
    request.url_for(route_name + ":detail", identity=self.identity, pk=pk)
  File "...venv/lib/python3.11/site-packages/starlette/requests.py", line 178, in url_for
    url_path = router.url_path_for(__name, **path_params)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "...venv/lib/python3.11/site-packages/starlette/routing.py", line 643, in url_path_for
    return route.url_path_for(__name, **path_params)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "...venv/lib/python3.11/site-packages/starlette/routing.py", line 434, in url_path_for
    url = route.url_path_for(remaining_name, **remaining_params)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "...venv/lib/python3.11/site-packages/starlette/routing.py", line 259, in url_path_for
    path, remaining_params = replace_params(
                             ^^^^^^^^^^^^^^^
  File "...venv/lib/python3.11/site-packages/starlette/routing.py", line 101, in replace_params
    value = convertor.to_string(value)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "...venv/lib/python3.11/site-packages/starlette/convertors.py", line 26, in to_string
    assert "/" not in value, "May not contain path separators"
           ^^^^^^^^^^^^^^^^
AssertionError: May not contain path separators
@sglebs sglebs added the bug Something isn't working label Jan 23, 2024
@sglebs
Copy link
Author

sglebs commented Feb 1, 2024

My workaround was to add a hash field and use a trigger to compute&set this hash field. I changed the composite primary key to use this hash of the URI instead of the URI itself. This way I avoid "/"/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

1 participant