Skip to content

FastAPI response model issue with SQLalchemy 1.4 #2124

@ArcLightSlavik

Description

@ArcLightSlavik

Checks

  • I added a descriptive title to this issue
  • I have searched (google, github) for similar issues and couldn't find anything
  • I have read and followed the docs and still think this is a bug

Not sure if this is the correct place for this, if not please notify where it would be better suited.

Bug

When using SQLAlchemy 1.3 with FastAPI and pydantic you could do this:

def get_users(db: Session, skip: int = 0, limit: int = 100):
    return db.query(models.User).offset(skip).limit(limit).all()


class UserBase(BaseModel):
    email: str


class User(UserBase):
    id: int
    is_active: bool
    items: List[Item] = []

    class Config:
        orm_mode = True


@app.get("/users/", response_model=List[schemas.User])
def read_users(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
    users = crud.get_users(db, skip=skip, limit=limit)
    return users

The result as expected is as follows:

[
  {
    "email": "string",
    "id": 1,
    "is_active": true,
    "items": []
  }
]

But when you upgrade to SQLAlchemy 1.4 the response_model breaks.

async def get_users(db: AsyncSession, skip: int = 0, limit: int = 100):
    db_execute = await db.execute(select(models.User).offset(skip).limit(limit))
    return db_execute.all()


@app.get("/users/", response_model=List[schemas.User])
async def read_users(skip: int = 0, limit: int = 100, db: AsyncSession = Depends(get_db)):
    crud_users = await crud.get_users(db, skip=skip, limit=limit)
    return crud_users
INFO:     127.0.0.1:60298 - "GET /users/?skip=0&limit=100 HTTP/1.1" 500 Internal Server Error
ERROR:    Exception in ASGI application
Traceback (most recent call last):
  File "/Users/user/code-utils/test_env/lib/python3.9/site-packages/uvicorn/protocols/http/h11_impl.py", line 389, in run_asgi
    result = await app(self.scope, self.receive, self.send)
  File "/Users/user/code-utils/test_env/lib/python3.9/site-packages/uvicorn/middleware/proxy_headers.py", line 45, in __call__
    return await self.app(scope, receive, send)
  File "/Users/user/code-utils/test_env/lib/python3.9/site-packages/fastapi/applications.py", line 179, in __call__
    await super().__call__(scope, receive, send)
  File "/Users/user/code-utils/test_env/lib/python3.9/site-packages/starlette/applications.py", line 111, in __call__
    await self.middleware_stack(scope, receive, send)
  File "/Users/user/code-utils/test_env/lib/python3.9/site-packages/starlette/middleware/errors.py", line 181, in __call__
    raise exc from None
  File "/Users/user/code-utils/test_env/lib/python3.9/site-packages/starlette/middleware/errors.py", line 159, in __call__
    await self.app(scope, receive, _send)
  File "/Users/user/code-utils/test_env/lib/python3.9/site-packages/starlette/exceptions.py", line 82, in __call__
    raise exc from None
  File "/Users/user/code-utils/test_env/lib/python3.9/site-packages/starlette/exceptions.py", line 71, in __call__
    await self.app(scope, receive, sender)
  File "/Users/user/code-utils/test_env/lib/python3.9/site-packages/starlette/routing.py", line 566, in __call__
    await route.handle(scope, receive, send)
  File "/Users/user/code-utils/test_env/lib/python3.9/site-packages/starlette/routing.py", line 227, in handle
    await self.app(scope, receive, send)
  File "/Users/user/code-utils/test_env/lib/python3.9/site-packages/starlette/routing.py", line 41, in app
    response = await func(request)
  File "/Users/user/code-utils/test_env/lib/python3.9/site-packages/fastapi/routing.py", line 190, in app
    response_data = await serialize_response(
  File "/Users/user/code-utils/test_env/lib/python3.9/site-packages/fastapi/routing.py", line 111, in serialize_response
    raise ValidationError(errors, field.type_)
pydantic.error_wrappers.ValidationError: 6 validation errors for User
response -> 0 -> email
  field required (type=value_error.missing)
response -> 0 -> id
  field required (type=value_error.missing)
response -> 0 -> is_active
  field required (type=value_error.missing)
response -> 1 -> email
  field required (type=value_error.missing)
response -> 1 -> id
  field required (type=value_error.missing)
response -> 1 -> is_active
  field required (type=value_error.missing)

A quick fix I found:

async def get_users(db: AsyncSession, skip: int = 0, limit: int = 100):
    db_execute = await db.execute(select(models.User).offset(skip).limit(limit))
    return_items = []
    for item in db_execute.all():
        return_items.append(item[0].__dict__)
    return return_items

Entire example can be found here: fastapi/fastapi#2331

Again I'm not sure if this is the correct repository for this, so please notify where would it be better suited.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bug V1Bug related to Pydantic V1.X

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions