You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 07:35:16