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

Flask+MongoEngine返回文档前如何统一转换_id与created_at字段格式

Flask + MongoEngine 文档返回字段通用转换方案

不推荐优先使用aggregate聚合实现这类格式转换,聚合返回结果是原生字典而非MongoEngine文档实例,会丢失模型自带的校验、自定义方法等能力,且每个查询都需要重复写投影转换逻辑,维护成本很高。以下是按推荐度排序的可行实现:


方案1:自定义抽象基类统一封装序列化逻辑(最优,全项目通用)

一次定义即可让所有继承基类的模型自动支持统一格式输出,完全保留ORM原生能力,后续格式调整只需要修改基类代码。

  1. 先定义所有业务模型共用的抽象基类
from mongoengine import Document, DateTimeField, EmbeddedDocument
from datetime import datetime

class BaseDocument(Document):
    meta = {'abstract': True}  # 标记为抽象类,不会生成独立数据集合

    def to_dict(self, time_fmt: str = "%Y-%m-%d %H:%M:%S"):
        data = self.to_mongo().to_dict()
        
        # 统一处理_id转id字符串
        if "_id" in data:
            data["id"] = str(data.pop("_id"))
        
        # 统一处理DateTime类型字段转字符串
        for field_name, field in self._fields.items():
            if field_name not in data:
                continue
            # 处理普通文档的时间字段
            if isinstance(field, DateTimeField):
                dt_val: datetime = getattr(self, field_name)
                # 如需返回毫秒时间戳,替换为 data[field_name] = str(int(dt_val.timestamp()*1000))
                data[field_name] = dt_val.strftime(time_fmt)
            # 递归处理嵌套嵌入式文档
            if isinstance(field, EmbeddedDocument):
                data[field_name] = getattr(self, field_name).to_dict()
        
        # 可在这里加全局排除字段逻辑,比如默认不返回敏感字段、文件字段
        # data.pop("file", None)
        return data
  1. 原有业务模型修改继承关系,字段定义完全不需要改动
class Effects(BaseDocument):
    name = StringField(unique=True, required=True,
                       min_length=2, max_length=20, validation=name_is_not_taken)
    parameters = EmbeddedDocumentListField(Parameter)
    parameterKeys = ListField(StringField(required=True, null=True))
    created_at = DateTimeField(default=datetime.now)
    created_by = StringField(required=True)
    effect_link = StringField(required=True)
    description = StringField(required=False)
    file = FileField(required=False)
  1. 路由调用时直接使用即可,支持单条、批量查询
# 查询单条
effect = Effects.objects.get(name="gain")
return effect.to_dict()

# 查询批量
effect_list = Effects.objects.all()
return [item.to_dict() for item in effect_list]

如果要实现接口直接返回文档对象自动序列化,可以自定义Flask全局JSON编码器,省去每次手动调用to_dict()的步骤:

import json
from flask import Flask
from mongoengine.base import BaseDocument as MongoBaseDoc

class CustomJSONEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, MongoBaseDoc):
            return obj.to_dict()
        return super().default(obj)

app = Flask(__name__)
app.json_encoder = CustomJSONEncoder

方案2:重写MongoEngine原生JSON序列化规则(轻量场景适用)

如果不想修改模型继承关系,可以直接在调用序列化方法时重写默认的扩展JSON输出规则,覆盖$oid、$date这类特殊结构的输出逻辑:

from bson import ObjectId
from datetime import datetime
import json

def custom_serializer(o):
    if isinstance(o, ObjectId):
        return str(o)
    if isinstance(o, datetime):
        return o.strftime("%Y-%m-%d %H:%M:%S")
    raise TypeError(f"Object of type {type(o)} is not JSON serializable")

# 使用时直接传入自定义序列化函数
effect = Effects.objects.get(name="gain")
return json.loads(json.dumps(effect.to_mongo(), default=custom_serializer))

该方案缺点是每个序列化场景都需要传入自定义规则,无法做到全局统一生效。


补充说明

你给出的期望返回示例中created_at值与id完全一致属于笔误,可根据实际业务需求,在基类的时间字段处理逻辑中调整为格式化时间字符串、ISO标准时间、毫秒级时间戳等任意格式。如果嵌入式文档列表中的子文档也需要做格式转换,只需要在基类的to_dict方法中增加列表类型判断,递归调用子文档的序列化方法即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.02 03:51:19