igorbenav / fastcrud

FastCRUD is a Python package for FastAPI, offering robust async CRUD operations and flexible endpoint creation utilities.
MIT License
644 stars 45 forks source link

SA error if there's no `deleted_at_column` when using delete CRUD operation #148

Open jd-solanki opened 1 month ago

jd-solanki commented 1 month ago

Describe the bug or question I've is_deleted col for soft delete but don't have deleted_at col that results in SA error: sqlalchemy.exc.CompileError: Unconsumed column names: deleted_at

To Reproduce Please provide a self-contained, minimal, and reproducible example of your use case

from collections.abc import AsyncGenerator
from contextlib import asynccontextmanager

from fastapi import Depends, FastAPI
from fastcrud import FastCRUD
from pydantic import BaseModel
from sqlalchemy import exc as sql_exec
from sqlalchemy import sql
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine
from sqlalchemy.orm import DeclarativeBase, Mapped, MappedAsDataclass, mapped_column

# --- DB
class Base(DeclarativeBase, MappedAsDataclass): ...

class Chat(Base):
    __tablename__ = "chats"

    id: Mapped[int] = mapped_column(primary_key=True, init=False)
    is_deleted: Mapped[bool] = mapped_column(default=False, server_default=sql.false(), kw_only=True)

engine = create_async_engine("sqlite+aiosqlite://")
async_session_maker = async_sessionmaker(bind=engine, expire_on_commit=False)

async def get_db() -> AsyncGenerator[AsyncSession, None]:
    async with async_session_maker() as session:
        yield session

async def create_tables():
    print("--- executing...")
    async with engine.begin() as conn:
        await conn.run_sync(Base.metadata.drop_all)
        print("--- creating tables...")
        await conn.run_sync(Base.metadata.create_all)

# --- Schemas
class ChatCreate(BaseModel): ...

class ChatUpdate(BaseModel): ...

class ChatUpdateInternal(BaseModel): ...

class ChatDelete(BaseModel): ...

FastCRUDChat = FastCRUD[
    Chat,
    ChatCreate,
    ChatUpdate,
    ChatUpdateInternal,
    ChatDelete,
]
chat_fastcrud = FastCRUDChat(Chat)

# --- FastAPI
@asynccontextmanager
async def lifespan(app: FastAPI):
    await create_tables()
    async with async_session_maker() as db:
        chat = Chat()
        db.add(chat)
        await db.commit()
        await db.refresh(chat)
    yield

app = FastAPI(lifespan=lifespan)

from fastapi import HTTPException

@app.delete("/chat/{id}")
async def delete_chat(id: int, db: AsyncSession = Depends(get_db)):
    try:
        return await chat_fastcrud.delete(db, id=id)
    except sql_exec.NoResultFound:
        raise HTTPException(status_code=404, detail="Chat not found")

Description Right now, When table don't have deleeted_at col FastCRUD raises exception however it should handle missing deleted_at col.

Screenshots None

Additional context It will be great if FastCRUD raises exception when result not found.