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

FastAPI对接MongoDB返回用户列表时触发Pydantic校验错误求助

问题背景

基于MongoDB与FastAPI搭建服务时,开发全量用户查询接口,返回多条文档即触发报错,查阅多份资料未定位根因,相关代码、存储数据样例、报错信息如下。

关联代码与数据

models.py

from pydantic import BaseModel, constr, Field

    #Class for a user
    class User(BaseModel):
       username: constr(to_lower=True)
       _id: str = Field(..., alias='id')
       name: str
       isActive : bool
       weekPlan : str

    #Example to provide on FastAPI Docs
    class Config:

        allow_population_by_field_name = True
        orm_mode = True
        schema_extra = {

        "example": {
            "name": "John Smith",
            "username": "john@smith.com",
            "isActive": "true",
            "weekPlan": "1234567",
        }
    }

routes.py

from fastapi import APIRouter, HTTPException, status, Response

from models.user import User
from config.db import dbusers

user = APIRouter()    

@user.get('/users', tags=["users"], response_model=list[User])
         async def find_all_users(response: Response):
         # Content-Range needed for react-admin
         response.headers['Content-Range'] = '4'
         response.headers['Access-Control-Expose-Headers'] = 'content-range'
         users = (dbusers.find())
         return users

MongoDB存储数据样例

{
    "_id" : ObjectId("62b325f65402e5ceea8a4b6f")
  },
  "name": "John Smith",
  "isActive" : true,
  "weekPlan" : "1234567"
   },
   {
    "_id" : ObjectId("62b325f65402e5ceea9a3d4c"),
    "username" : "john@smith.com",
    "name" : "John Smith",
    "isActive" : true,
    "weekPlan" : "1234567"
    }

报错信息

await self.app(scope, receive, send)
  File "C:\Git2\thrive-app-react\backend\venv\lib\site-packages\starlette\routing.py", line 670, in __call__
    await route.handle(scope, receive, send)
  File "C:\Git2\thrive-app-react\backend\venv\lib\site-packages\starlette\routing.py", line 266, in handle
    await self.app(scope, receive, send)
  File "C:\Git2\thrive-app-react\backend\venv\lib\site-packages\starlette\routing.py", line 65, in app
    response = await func(request)
  File "C:\Git2\thrive-app-react\backend\venv\lib\site-packages\fastapi\routing.py", line 235, in app
    response_data = await serialize_response(
  File "C:\Git2\thrive-app-react\backend\venv\lib\site-packages\fastapi\routing.py", line 138, in serialize_response
    raise ValidationError(errors, field.type_)
pydantic.error_wrappers.ValidationError: 1 validation error for User
response
  value is not a valid list (type=type_error.list)
问题根因
  • 核心触发点:dbusers.find() 返回的是MongoDB的游标(Cursor)可迭代对象,不是Python原生列表类型,FastAPI做响应序列化时,无法将游标匹配到接口声明的list[User]响应模型,直接抛出类型校验错误。
  • 模型定义缩进错误:Config类定义在User类外部,配置的allow_population_by_field_name、orm_mode等规则完全不生效,字段别名映射逻辑失效。
  • 字段映射配置错误:MongoDB返回的主键字段名为_id,代码中将字段别名设为id,和实际返回字段名不匹配;且MongoDB默认返回的_id是ObjectId类型,不是字符串,Pydantic无法直接做类型转换。
  • 脏数据问题:样例中第一条用户文档缺失必填的username字段,即使类型问题修复,查询到该条数据时仍会触发字段校验错误。
  • 响应头格式错误:Content-Range头赋值为'4'不符合HTTP规范,react-admin无法正确解析,标准格式应为items 起始索引-结束索引/总条数。
  • 示例值错误:模型schema_extra中isActive字段写为字符串"true",和声明的bool类型冲突,会导致接口文档示例展示异常。
修复方案
  1. 修正models.py代码,调整缩进将Config类放入User类内部,新增ObjectId类型转换逻辑,修正字段映射与示例值:
from pydantic import BaseModel, constr, Field
from bson import ObjectId

# 自定义MongoDB ObjectId类型校验,自动转成字符串
class PyObjectId(str):
    @classmethod
    def __get_validators__(cls):
        yield cls.validate

    @classmethod
    def validate(cls, v):
        if not isinstance(v, ObjectId):
            raise TypeError("must be a valid ObjectId")
        return str(v)

class User(BaseModel):
    username: constr(to_lower=True)
    id: PyObjectId = Field(..., alias="_id")
    name: str
    isActive: bool
    weekPlan: str

    class Config:
        allow_population_by_field_name = True
        orm_mode = True
        schema_extra = {
            "example": {
                "name": "John Smith",
                "username": "john@smith.com",
                "isActive": True,
                "weekPlan": "1234567",
            }
        }
  1. 修正routes.py逻辑,将MongoDB游标转为原生列表,按照规范设置Content-Range响应头:
from fastapi import APIRouter, Response
from models.user import User
from config.db import dbusers

user = APIRouter()    

@user.get('/users', tags=["users"], response_model=list[User])
async def find_all_users(response: Response):
    # 将游标转为原生Python列表
    users = list(dbusers.find())
    total = len(users)
    # 按照规范设置Content-Range头
    response.headers['Content-Range'] = f'items 0-{total-1}/{total}'
    response.headers['Access-Control-Expose-Headers'] = 'content-range'
    return users
  1. 清理MongoDB中的脏数据,为缺失username字段的用户文档补全对应字段值,避免必填字段缺失触发校验错误。

内容的提问来源于stack exchange,提问作者Chris Taverner

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 21:03:40