Based on the docs, I expect a DTO given to a handler via the dto
parameter to apply to the return value when no return_dto
is specified. However, when the SQLALchemyPlugin
is used this is not the case.
More generally, it looks like the return_dto
resolution order in the BaseRouteHandler
puts generated DTOs from serialization plugins ahead of DTOs from explicit dto
parameters on the handler. For me this is not the expected behavior based on the documentation, though I can see that one may want to opt-in to the return_dto
provided by the plugin while still providing a data DTO, which perhaps is the reason for the current behavior.
It may be that the docs just need an update to clarify? An alternative might be to specify a particular value that can be passed to the return_dto
parameter to maintain the current behavior, but to otherwise resolve to dto
if it's given.
No response
from typing import Annotated
from advanced_alchemy.extensions.litestar.plugins.init.config.engine import EngineConfig
from litestar import Litestar, get
from litestar.contrib.sqlalchemy.dto import SQLAlchemyDTO
from litestar.contrib.sqlalchemy.plugins import (
SQLAlchemyPlugin,
SQLAlchemySyncConfig,
)
from litestar.dto import DTOConfig
from litestar.testing import TestClient
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
class Base(DeclarativeBase):
pass
class Address(Base):
__tablename__ = "addresses"
id: Mapped[int] = mapped_column(primary_key=True)
street: Mapped[str]
city: Mapped[str]
state: Mapped[str]
zip: Mapped[str]
db_config = SQLAlchemySyncConfig(
connection_string="sqlite://",
engine_config=EngineConfig(echo=False),
create_all=True,
)
AddressDTO = SQLAlchemyDTO[
Annotated[
Address,
DTOConfig(exclude={"zip"}),
]
]
@get("/address", dto=AddressDTO, sync_to_thread=False)
def get_address() -> Address:
return Address(id=1, street="123 Main St", city="Anytown", state="NY", zip="12345")
app_with_plugin = Litestar(
route_handlers=[get_address],
plugins=[SQLAlchemyPlugin(config=db_config)],
)
app_without_plugin = Litestar(
route_handlers=[get_address],
)
with TestClient(app=app_without_plugin) as client:
if "zip" not in client.get("/address").json():
print("'zip' is properly excluded without the plugin")
with TestClient(app=app_with_plugin) as client:
if "zip" in client.get("/address").json():
print("'zip' is not excluded with the plugin")
1. Run the MCVE
2. Note that 'zip' is not properly excluded from the response JSON when the `SQLALchemyPlugin` is used.
"![SCREENSHOT_DESCRIPTION](SCREENSHOT_LINK.png)"
'zip' is properly excluded without the plugin
INFO - 2024-05-02 11:27:19,957 - httpx - _client - HTTP Request: GET http://testserver.local/address "HTTP/1.1 200 OK"
'zip' is not excluded with the plugin
INFO - 2024-05-02 11:27:19,968 - httpx - _client - HTTP Request: GET http://testserver.local/address "HTTP/1.1 200 OK"
2.8.2
Pay now to fund the work behind this issue.
Get updates on progress being made.
Maintainer is rewarded once the issue is completed.
You're funding impactful open source efforts
You want to contribute to this effort
You want to get funding like this too