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: SQLAlchemy models with default=func.now() error with Unexpected default type #3457

Open
1 of 4 tasks
MitPitt opened this issue May 1, 2024 · 3 comments
Open
1 of 4 tasks
Labels
Bug 🐛 This is something that is not working as expected

Comments

@MitPitt
Copy link

MitPitt commented May 1, 2024

Description

Columns with default=func.now() will cause SQLAlchemySerializationPlugin to error with ValueError: Unexpected default type.

Workaround is replacing it with server_default=func.now()

URL to code causing the issue

No response

MCVE

from litestar import Litestar, get
from litestar.contrib.sqlalchemy.plugins import SQLAlchemySerializationPlugin
from sqlalchemy import BigInteger, Column, DateTime, String
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.sql import func


class Base(DeclarativeBase): ...


class User(Base):
    __tablename__ = "users"
    id = Column(BigInteger, primary_key=True)
    username = Column(String)
    first_interaction = Column(DateTime, default=func.now())
    last_interaction = Column(DateTime, default=func.now(), onupdate=func.now())


@get("/{user_id:int}")
async def get_user(user_id: int) -> User:
    user = "pretend this is a User"
    return user


app = Litestar(
    route_handlers=[get_user],
    plugins=[SQLAlchemySerializationPlugin()],
)

Steps to reproduce

No response

Screenshots

No response

Logs

Traceback (most recent call last):
  File "/home/mit/frbot/./asd.py", line 25, in <module>
    app = Litestar(
          ^^^^^^^^^
  File "/home/mit/frbot/.venv/lib/python3.11/site-packages/litestar/app.py", line 483, in __init__
    self.register(route_handler)
  File "/home/mit/frbot/.venv/lib/python3.11/site-packages/litestar/app.py", line 667, in register
    route_handler.on_registration(self)
  File "/home/mit/frbot/.venv/lib/python3.11/site-packages/litestar/handlers/http_handlers/base.py", line 560, in on_registration
    super().on_registration(app)
  File "/home/mit/frbot/.venv/lib/python3.11/site-packages/litestar/handlers/base.py", line 544, in on_registration
    self.resolve_return_dto()
  File "/home/mit/frbot/.venv/lib/python3.11/site-packages/litestar/handlers/base.py", line 504, in resolve_return_dto
    return_dto.create_for_field_definition(
  File "/home/mit/frbot/.venv/lib/python3.11/site-packages/litestar/dto/base_dto.py", line 183, in create_for_field_definition
    backend_context[key] = backend_cls(  # type: ignore[literal-required]
                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/mit/frbot/.venv/lib/python3.11/site-packages/litestar/dto/_codegen_backend.py", line 72, in __init__
    super().__init__(
  File "/home/mit/frbot/.venv/lib/python3.11/site-packages/litestar/dto/_backend.py", line 108, in __init__
    self.parsed_field_definitions = self.parse_model(
                                    ^^^^^^^^^^^^^^^^^
  File "/home/mit/frbot/.venv/lib/python3.11/site-packages/litestar/dto/_backend.py", line 140, in parse_model
    for field_definition in self.dto_factory.generate_field_definitions(model_type):
  File "/home/mit/frbot/.venv/lib/python3.11/site-packages/advanced_alchemy/extensions/litestar/dto.py", line 280, in generate_field_definitions
    yield from cls.handle_orm_descriptor(
               ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/functools.py", line 946, in _method
    return method.__get__(obj, cls)(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/mit/frbot/.venv/lib/python3.11/site-packages/advanced_alchemy/extensions/litestar/dto.py", line 116, in _
    default, default_factory = _detect_defaults(elem)
                               ^^^^^^^^^^^^^^^^^^^^^^
  File "/home/mit/frbot/.venv/lib/python3.11/site-packages/advanced_alchemy/extensions/litestar/dto.py", line 311, in _detect_defaults
    raise ValueError(msg)
ValueError: Unexpected default type

Litestar Version

sqlalchemy = { extras = ["asyncio"], version = "^2.0.29" }
litestar = {extras = ["sqlalchemy", "standard"], version = "^2.8.2"}

Platform

  • Linux
  • Mac
  • Windows
  • Other (Please specify in the description above)

Note

While we are open for sponsoring on GitHub Sponsors and
OpenCollective, we also utilize Polar.sh to engage in pledge-based sponsorship.

Check out all issues funded or available for funding on our Polar.sh dashboard

  • If you would like to see an issue prioritized, make a pledge towards it!
  • We receive the pledge once the issue is completed & verified
  • This, along with engagement in the community, helps us know which features are a priority to our users.
Fund with Polar
@MitPitt MitPitt added the Bug 🐛 This is something that is not working as expected label May 1, 2024
@sion908
Copy link

sion908 commented May 1, 2024

@MitPitt
Hi!!
I also encountered the same error.
And this seems to be an expected error.
As a concept like a responsibility branching point, it seems better not to let the db generate values ​​as much as possible, so use lambda: datetime.now(timezone.utc) instead of func.nou(). However, in this case, I think it can be said that it is good.

like this
And it's in hereadvanced-alchemy

class AuditColumns:
    """Created/Updated At Fields Mixin."""

    created_at: Mapped[datetime] = mapped_column(
        DateTimeUTC(timezone=True),
        default=lambda: datetime.now(timezone.utc),
    )
    """Date/time of instance creation."""
    updated_at: Mapped[datetime] = mapped_column(
        DateTimeUTC(timezone=True),
        default=lambda: datetime.now(timezone.utc),
    )
    """Date/time of instance last update."""

you can import from here

from litestar.plugins.sqlalchemy import AuditColumns

@MitPitt
Copy link
Author

MitPitt commented May 1, 2024

@sion908 I am introducing litestar to a project which already had sqlalchemy in it. I would like to keep new litestar code separated from older sqlalchemy stuff. Meaning I don't want to import AuditColumns from litestar. In case I wanted to remove litestar from the project in the future, this would create problems.

But I will try using lambda: datetime.now(), thanks.

@sion908
Copy link

sion908 commented May 1, 2024

I hope it was helpful

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug 🐛 This is something that is not working as expected
Projects
None yet
Development

No branches or pull requests

2 participants