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

FastAPI端点返回含datetime的字典序列化报错的优化方案咨询

FastAPI datetime序列化问题及优化方案

问题描述

基于Python 3.11.2搭建FastAPI 0.103.1版本的API时,遇到序列化异常:

  • 直接返回包含datetime对象的字典字面量时,前端能正常接收格式正确的日期数据;
  • 将相同结构的字典赋值给变量后返回,后端会抛出TypeError: Object of type datetime is not JSON serializable错误。
    目前采用return json.loads(json.dumps(model_metadata, default=str))临时解决,但写法冗余,需更优方案。

直接返回正常的写法

@router.post("")
async def post_endpoint(payload: dict):
    ...
    return {
        'train_dates': {
            'start_date': datetime.datetime(2018, 1, 1, 0, 0),
            'end_date': datetime.datetime(2018, 1, 25, 0, 0)
        },
        'test_dates': {
            'start_date': datetime.datetime(2018, 1, 25, 0, 0),
            'end_date': datetime.datetime(2018, 2, 1, 0, 0)
        },
        'forecast_dates': {
            'start_date': datetime.datetime(2018, 2, 1, 0, 0),
            'end_date': datetime.datetime(2018, 2, 14, 0, 0)
        }
    }

返回变量报错的写法

@router.post("")
async def post_endpoint(payload: dict):
    ...
    model_metadata = {
        'train_dates': {
            'start_date': datetime.datetime(2018, 1, 1, 0, 0),
            'end_date': datetime.datetime(2018, 1, 25, 0, 0)
        },
        'test_dates': {
            'start_date': datetime.datetime(2018, 1, 25, 0, 0),
            'end_date': datetime.datetime(2018, 2, 1, 0, 0)
        },
        'forecast_dates': {
            'start_date': datetime.datetime(2018, 2, 1, 0, 0),
            'end_date': datetime.datetime(2018, 2, 14, 0, 0)
        }
    }
    return model_metadata

当前冗余解决方法

return json.loads(json.dumps(model_metadata, default=str))

优化方案

方案1:使用Pydantic模型(推荐)

FastAPI底层依赖Pydantic处理数据序列化,定义对应模型后会自动完成datetime对象的序列化,还能提供数据校验功能。

from pydantic import BaseModel
import datetime

# 定义日期范围嵌套模型
class DateRange(BaseModel):
    start_date: datetime.datetime
    end_date: datetime.datetime

# 定义元数据模型
class ModelMetadata(BaseModel):
    train_dates: DateRange
    test_dates: DateRange
    forecast_dates: DateRange

@router.post("")
async def post_endpoint(payload: dict):
    ...
    model_metadata = ModelMetadata(
        train_dates=DateRange(
            start_date=datetime.datetime(2018, 1, 1, 0, 0),
            end_date=datetime.datetime(2018, 1, 25, 0, 0)
        ),
        test_dates=DateRange(
            start_date=datetime.datetime(2018, 1, 25, 0, 0),
            end_date=datetime.datetime(2018, 2, 1, 0, 0)
        ),
        forecast_dates=DateRange(
            start_date=datetime.datetime(2018, 2, 1, 0, 0),
            end_date=datetime.datetime(2018, 2, 14, 0, 0)
        )
    )
    return model_metadata

方案2:自定义全局JSON编码器

在FastAPI实例初始化时指定自定义JSON编码器,统一处理datetime对象的序列化逻辑,无需在每个端点重复处理。

from fastapi import FastAPI
import datetime
from json import JSONEncoder

# 自定义编码器
class CustomJSONEncoder(JSONEncoder):
    def default(self, obj):
        if isinstance(obj, datetime.datetime):
            return obj.isoformat()  # 可根据需求调整格式,如str(obj)
        return super().default(obj)

# 初始化FastAPI时传入编码器
app = FastAPI(json_encoder=CustomJSONEncoder)

# 路由端点写法不变
@router.post("")
async def post_endpoint(payload: dict):
    ...
    model_metadata = {
        'train_dates': {
            'start_date': datetime.datetime(2018, 1, 1, 0, 0),
            'end_date': datetime.datetime(2018, 1, 25, 0, 0)
        },
        'test_dates': {
            'start_date': datetime.datetime(2018, 1, 25, 0, 0),
            'end_date': datetime.datetime(2018, 2, 1, 0, 0)
        },
        'forecast_dates': {
            'start_date': datetime.datetime(2018, 2, 1, 0, 0),
            'end_date': datetime.datetime(2018, 2, 14, 0, 0)
        }
    }
    return model_metadata

方案3:手动转换datetime为字符串(轻量方案)

构建字典时直接将datetime对象转换为符合需求的字符串格式,避免后续序列化报错,适合简单场景。

@router.post("")
async def post_endpoint(payload: dict):
    ...
    model_metadata = {
        'train_dates': {
            'start_date': datetime.datetime(2018, 1, 1, 0, 0).isoformat(),
            'end_date': datetime.datetime(2018, 1, 25, 0, 0).isoformat()
        },
        'test_dates': {
            'start_date': datetime.datetime(2018, 1, 25, 0, 0).isoformat(),
            'end_date': datetime.datetime(2018, 2, 1, 0, 0).isoformat()
        },
        'forecast_dates': {
            'start_date': datetime.datetime(2018, 2, 1, 0, 0).isoformat(),
            'end_date': datetime.datetime(2018, 2, 14, 0, 0).isoformat()
        }
    }
    return model_metadata

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 15:37:09