-
Notifications
You must be signed in to change notification settings - Fork 85
Closed
Description
Describe the bug or question
If filter of non-string column contains an operator, the request will fail.
To Reproduce
Example (the necessary data has already been inserted into the table in lifetime):
import datetime
from typing import AsyncGenerator
from fastapi import FastAPI
from fastcrud import crud_router
from pydantic import BaseModel
from sqlalchemy import Column, DateTime, Integer, Numeric, String, func, insert, select
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
from sqlalchemy.orm import DeclarativeBase, sessionmaker
class Base(DeclarativeBase):
pass
class Item(Base):
__tablename__ = "items"
id = Column(Integer, primary_key=True)
name = Column(String)
description = Column(String)
category = Column(String)
price = Column(Numeric)
last_sold = Column(DateTime)
created_at = Column(DateTime, default=func.now())
class ItemSchema(BaseModel):
name: str | None = None
description: str | None = None
category: str | None = None
price: float | None = None
last_sold: datetime.datetime | None = None
DATABASE_URL = "postgresql+asyncpg://user:password@host:port/database"
engine = create_async_engine(DATABASE_URL, echo=True)
async_session = sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)
# Database session dependency
async def get_session() -> AsyncGenerator[AsyncSession, None]:
async with async_session() as session:
yield session
# Create tables before the app start
async def lifespan(app: FastAPI):
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all)
# Filling table
count = (await conn.execute(select(func.count()).select_from(Item))).scalar()
if count != 10:
for i in range(10):
await conn.execute(insert(Item).values(price=i))
yield
# FastAPI app
app = FastAPI(lifespan=lifespan)
item_router = crud_router(
session=get_session,
model=Item,
create_schema=ItemSchema,
update_schema=ItemSchema,
path="/items",
tags=["Items"],
filter_config={
"price": None,
"price__gt": None,
},
)
app.include_router(item_router)
Run with uvicorn main:app
Perform GET request on http://localhost:8000/items?price__gt=5
Description
Request with a filter and any operator leads to error, due to the lack of cast before querying to database.
sqlalchemy.exc.ProgrammingError: (sqlalchemy.dialects.postgresql.asyncpg.ProgrammingError) <class 'asyncpg.exceptions.UndefinedFunctionError'>: operator does not exist: numeric > character varying
HINT: No operator matches the given name and argument types. You might need to add explicit type casts.
[SQL: SELECT items.id, items.name, items.description, items.category, items.price, items.last_sold, items.created_at
FROM items
WHERE items.price > $1::VARCHAR
LIMIT $2::INTEGER]
[parameters: ('5', 100)]
Additional context
Please note that it is necessary to use a database backend with strict type comparison (e.g. PostgreSQL).
Metadata
Metadata
Assignees
Labels
No labels