FastAPI在Docker容器中返回数据库查询结果触发类型错误求助
问题描述
编写的FastAPI接口在本地虚拟环境及远程Linux服务器上运行正常,但在本地Docker容器中运行时触发序列化类型错误,已尝试卸载重装Docker,问题仍未解决,需排查并解决该问题。
接口代码
@api.get("/subject") async def subject_type_list( db: get_db = Depends(), ) : data = ( db.query( SubjectType.id, SubjectType.name, SubjectType.korean_name, ).filter( SubjectType.subject_list_id == 1 ).all() ) return data
错误信息
fastapi | Traceback (most recent call last): fastapi | File "/usr/local/lib/python3.10/site-packages/fastapi/encoders.py", line 152, in jsonable_encoder fastapi | data = dict(obj) fastapi | TypeError: cannot convert dictionary update sequence element #0 to a sequence fastapi | fastapi | During handling of the above exception, another exception occurred: fastapi | fastapi | Traceback (most recent call last): fastapi | File "/usr/local/lib/python3.10/site-packages/fastapi/encoders.py", line 157, in jsonable_encoder fastapi | data = vars(obj) fastapi | TypeError: vars() argument must have __dict__ attribute fastapi | fastapi | The above exception was the direct cause of the following exception: fastapi | fastapi | Traceback (most recent call last): fastapi | File "/usr/local/lib/python3.10/site-packages/uvicorn/protocols/http/httptools_impl.py", line 419, in run_asgi fastapi | result = await app( # type: ignore[func-returns-value] fastapi | File "/usr/local/lib/python3.10/site-packages/uvicorn/middleware/proxy_headers.py", line 78, in __call__ fastapi | return await self.app(scope, receive, send) fastapi | File "/usr/local/lib/python3.10/site-packages/fastapi/applications.py", line 271, in __call__ fastapi | await super().__call__(scope, receive, send) fastapi | File "/usr/local/lib/python3.10/site-packages/starlette/applications.py", line 125, in __call__ fastapi | await self.middleware_stack(scope, receive, send) fastapi | File "/usr/local/lib/python3.10/site-packages/starlette/middleware/errors.py", line 184, in __call__ fastapi | raise exc fastapi | File "/usr/local/lib/python3.10/site-packages/starlette/middleware/errors.py", line 162, in __call__ fastapi | await self.app(scope, receive, _send) fastapi | File "/usr/local/lib/python3.10/site-packages/starlette/middleware/base.py", line 104, in __call__ fastapi | response = await self.dispatch_func(request, call_next) fastapi | File "/code/./app/main.py", line 81, in log_request fastapi | res = await call_next(req) fastapi | File "/usr/local/lib/python3.10/site-packages/starlette/middleware/base.py", line 80, in call_next fastapi | raise app_exc fastapi | File "/usr/local/lib/python3.10/site-packages/starlette/middleware/base.py", line 69, in coro fastapi | await self.app(scope, receive_or_disconnect, send_no_error) fastapi | File "/usr/local/lib/python3.10/site-packages/starlette/middleware/cors.py", line 84, in __call__ fastapi | await self.app(scope, receive, send) fastapi | File "/usr/local/lib/python3.10/site-packages/starlette/middleware/exceptions.py", line 79, in __call__ fastapi | raise exc fastapi | File "/usr/local/lib/python3.10/site-packages/starlette/middleware/exceptions.py", line 68, in __call__ fastapi | await self.app(scope, receive, sender) fastapi | File "/usr/local/lib/python3.10/site-packages/fastapi/middleware/asyncexitstack.py", line 26, in __call__ fastapi | raise dependency_exception fastapi | File "/code/./app/db/deps.py", line 9, in get_db fastapi | yield db fastapi | ValueError: [TypeError('cannot convert dictionary update sequence element #0 to a sequence'), TypeError('vars() argument must have __dict__ attribute')]
Docker配置文件
Dockerfile
FROM python:3.8 WORKDIR /code COPY requirement/dev.txt . RUN pip install --no-cache-dir --upgrade -r /code/dev.txt RUN mkdir app COPY app app ENV PYTHONUNBUFFERED 1 ENV PYTHONDONTWRITEBYTECODE 1
docker-compose.yml
#docker-compose.yml fastapi: container_name: fastapi build: context: . dockerfile: Dockerfile ports: - 5000:5000 command: > bash -c "uvicorn app.main:app --host 0.0.0.0 --port 5000 --reload" volumes: - ./app:/code/app env_file: - env/dev.env
排查与解决方法
1. 核心问题定位
错误源于FastAPI的jsonable_encoder序列化环节:直接返回SQLAlchemy查询的元组对象(query(列1,列2).all()返回元组列表),而Docker环境中FastAPI/Starlette的序列化逻辑对这类对象的处理和本地环境存在差异,大概率是依赖版本不一致导致。
2. 直接修复方案
将查询结果转换为可序列化的字典格式,两种方式任选:
方式一:用SQLAlchemy内置方法(1.4+版本适用)
@api.get("/subject") async def subject_type_list( db: get_db = Depends(), ) : data = ( db.query( SubjectType.id, SubjectType.name, SubjectType.korean_name, ).filter( SubjectType.subject_list_id == 1 ).all() ) # 转换为字典列表 return [item._asdict() for item in data]
方式二:手动构造字典(兼容性强)
@api.get("/subject") async def subject_type_list( db: get_db = Depends(), ) : data = ( db.query( SubjectType.id, SubjectType.name, SubjectType.korean_name, ).filter( SubjectType.subject_list_id == 1 ).all() ) # 手动映射字段 return [{"id": item[0], "name": item[1], "korean_name": item[2]} for item in data]
3. 环境一致性排查
检查依赖版本差异
本地和Docker环境依赖版本不一致是常见诱因:
- 本地执行
pip freeze > local_requirements.txt - 进入Docker容器执行
pip freeze > docker_requirements.txt - 对比两个文件,重点核对
fastapi、starlette、sqlalchemy的版本,修改dev.txt后重新构建镜像。
优化Dockerfile逻辑
当前Dockerfile先创建app目录再复制代码,可能导致依赖安装后代码覆盖问题,调整顺序:
FROM python:3.8 WORKDIR /code # 先复制依赖文件,利用Docker缓存 COPY requirement/dev.txt . RUN pip install --no-cache-dir --upgrade -r /code/dev.txt # 再复制整个项目代码,无需手动创建app目录 COPY . . ENV PYTHONUNBUFFERED 1 ENV PYTHONDONTWRITEBYTECODE 1
4. 验证方法
修改代码后,重新构建并启动容器:
docker-compose build fastapi docker-compose up fastapi
调用接口验证是否正常返回JSON数据。
内容的提问来源于stack exchange,提问作者jung
相关产品推荐
相关产品推荐

