Fastapi: [BUG] When i try to response a sqlalchemy model then i get ValidationError

Created on 2 Jul 2019  路  7Comments  路  Source: tiangolo/fastapi

Describe the bug
I have simple user creation method:

    def create(self, login: str, password: str) -> models.User:
        if not password:
            raise RuntimeError('Password is empty')

        password = utils.generate_password_hash(password)

        user = models.User(login=login, password=password)
        session = self.session()
        session.add(user)
        session.commit()
        session.refresh(user)
        return user

and the model:

class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)
    login = Column(String(256))
    password = Column(String(1000))

    created_at = Column(DateTime, default=datetime.datetime.now)
    updated_at = Column(DateTime, default=datetime.datetime.now, onupdate=datetime.datetime.now)
    removed_at = Column(DateTime, nullable=True)

and this endpoint:


@router.post('/users', status_code=HTTP_201_CREATED, response_model=schemas.UserOut)
def create_user(
        user_data: schemas.UserIn,
        res: UsersResource = Depends(get_users_res)
):
    user = res.create(user_data.login, user_data.password.get_secret_value())
    return user

with schemas:

class UserIn(BaseModel):
    login: str = Schema(..., min_length=3)
    password: SecretStr = Schema(..., min_length=5)

class UserOut(BaseModel):
    id: int
    login: str
    created_at: datetime.datetime
    updated_at: datetime.datetime
    removed_at: Optional[datetime.datetime]

When I try to make a request it falls with the following error:

core_1      | email-validator not installed, email fields will be treated as str.
core_1      | To install, run: pip install email-validator
core_1      | INFO: Started server process [1]
core_1      | INFO: Waiting for application startup.
core_1      | INFO: Uvicorn running on http://0.0.0.0:8080 (Press CTRL+C to quit)
core_1      | INFO: ('172.21.0.1', 44266) - "POST /api/v1/users HTTP/1.1" 500
core_1      | ERROR: Exception in ASGI application
core_1      | Traceback (most recent call last):
core_1      |   File "/usr/local/lib/python3.7/site-packages/uvicorn/protocols/http/httptools_impl.py", line 370, in run_asgi
core_1      |     result = await app(self.scope, self.receive, self.send)
core_1      |   File "/usr/local/lib/python3.7/site-packages/uvicorn/middleware/proxy_headers.py", line 37, in __call__
core_1      |     await self.app(scope, receive, send)
core_1      |   File "/usr/local/lib/python3.7/site-packages/starlette/applications.py", line 133, in __call__
core_1      |     await self.error_middleware(scope, receive, send)
core_1      |   File "/usr/local/lib/python3.7/site-packages/starlette/middleware/errors.py", line 122, in __call__
core_1      |     raise exc from None
core_1      |   File "/usr/local/lib/python3.7/site-packages/starlette/middleware/errors.py", line 100, in __call__
core_1      |     await self.app(scope, receive, _send)
core_1      |   File "/usr/local/lib/python3.7/site-packages/starlette/exceptions.py", line 73, in __call__
core_1      |     raise exc from None
core_1      |   File "/usr/local/lib/python3.7/site-packages/starlette/exceptions.py", line 62, in __call__
core_1      |     await self.app(scope, receive, sender)
core_1      |   File "/usr/local/lib/python3.7/site-packages/starlette/routing.py", line 585, in __call__
core_1      |     await route(scope, receive, send)
core_1      |   File "/usr/local/lib/python3.7/site-packages/starlette/routing.py", line 207, in __call__
core_1      |     await self.app(scope, receive, send)
core_1      |   File "/usr/local/lib/python3.7/site-packages/starlette/routing.py", line 40, in app
core_1      |     response = await func(request)
core_1      |   File "/usr/local/lib/python3.7/site-packages/fastapi/routing.py", line 122, in app
core_1      |     skip_defaults=response_model_skip_defaults,
core_1      |   File "/usr/local/lib/python3.7/site-packages/fastapi/routing.py", line 54, in serialize_response
core_1      |     raise ValidationError(errors)
core_1      | pydantic.error_wrappers.ValidationError: 1 validation error
core_1      | response
core_1      |   value is not a valid dict (type=type_error.dict)

This happens only when the database is postgresql. When i use sqlite everything works fine.

Environment:

  • OS: Linux fortdey 5.1.15-arch1-1-ARCH #1 SMP PREEMPT Tue Jun 25 04:49:39 UTC 2019 x86_64 GNU/Linux and docker container with last Ubuntu

    • Fastapi 0.29.1

    • Python Python 3.7.3

bug

Most helpful comment

Use the 'orm_mode' flag in the pydantic schema, it's a new thing in the latest pydantic release. See pydantic docs for details. Solved the issue for me

All 7 comments

I'm using dirty hack now

def model2dict(row):
    d = {}
    for column in row.__table__.columns:
        d[column.name] = getattr(row, column.name)

    return d
...
  ...endpoint...
  return model2dict(user)

it works fine.

Are you sure one of your non-nullable model fields isn't passed a null value by SQLAlchemy? I was dealing with a similar error the other day and this turned out to be the cause, although "value is not a valid dict" seems to be a generic Pydantic error.

Use the 'orm_mode' flag in the pydantic schema, it's a new thing in the latest pydantic release. See pydantic docs for details. Solved the issue for me

Use the 'orm_mode' flag in the pydantic schema, it's a new thing in the latest pydantic release. See pydantic docs for details. Solved the issue for me

@bharling You are a gentleman and a scholar! This has been driving me nuts for ever (well, hours, but time is money).

Thanks for the solution!

Thanks for the help here @sm-Fifteen, @bharling!

Thanks @deterok for reporting back and closing the issue.

Great to see I wasn't the only one.. I'd thought that orm_mode was only for actual SQLAlchemy models themselves, not just result proxies

Was this page helpful?
0 / 5 - 0 ratings