Python Eve无Schema环境下ObjectId字符串过滤及字段提交问题
我之前在使用Eve搭建无Schema数据服务时,也碰到过一模一样的问题,给你梳理一下可行的解决方案:
一、无Schema时如何提交ObjectId类型字段?
因为你开启了ALLOW_UNKNOWN=True且未定义Schema,Eve默认会把所有提交的字符串字段存成普通字符串类型,而非MongoDB的ObjectId类型。这就导致你存入的app_id是字符串,但关联的apps集合_id是ObjectId,后续查询自然匹配不上。
解决方法是用**Eve的钩子(hook)**在数据插入前,把指定字段转换成ObjectId:
from bson.objectid import ObjectId from eve import Eve # 初始化Eve应用,开启ALLOW_UNKNOWN app = Eve(ALLOW_UNKNOWN=True) # 定义钩子函数:在tasks集合插入前转换app_id为ObjectId def pre_insert_tasks_hook(items): for item in items: # 检查是否存在app_id字段,存在则转换类型 if 'app_id' in item: item['app_id'] = ObjectId(item['app_id']) # 绑定钩子到tasks集合的pre_insert事件 app.on_pre_insert_tasks += pre_insert_tasks_hook if __name__ == '__main__': app.run()
配置后,当你POST /tasks提交{"title": "Check log files", "app_id": "5afb45872fb8d57c7d93d8fa"}时,Eve会自动把app_id转换成ObjectId类型存入MongoDB,和apps集合的_id类型保持一致。
二、如何通过app_id过滤查询tasks数据?
解决了存储类型问题后,查询有两种可行方式:
方式1:开启全局QUERY_OBJECTID_AS_STRING配置
在Eve的配置中添加:
QUERY_OBJECTID_AS_STRING = True
这个配置会让Eve自动把查询参数中的字符串转换成ObjectId去匹配MongoDB中的字段。此时你直接用原来的查询方式就能得到结果:
GET '/tasks/?where={"app_id": "5afb45872fb8d57c7d93d8fa"}'
关于你遇到的404问题:
开启该配置后访问单个文档(比如GET /tasks/5afb4bcd2fb8d57c7d93d912)返回404,大概率是以下原因:
- 确认
tasks集合中的_id确实是ObjectId类型(Eve默认自动生成,若手动指定为字符串则会出错) - 确保配置是全局的
QUERY_OBJECTID_AS_STRING = True,而非在DOMAIN的集合配置里错误设置 - 检查请求的
_id字符串是否完全匹配MongoDB中的文档ID
方式2:使用MongoDB的$oid操作符查询
如果你不想修改全局配置,可以直接在查询条件中用$oid包裹ObjectId字符串,明确告诉MongoDB要匹配ObjectId类型:
GET '/tasks/?where={"app_id": {"$oid": "5afb45872fb8d57c7d93d8fa"}}'
这种方式无需额外配置,直接就能匹配到存储为ObjectId类型的app_id字段。
总结
核心问题是字段类型不匹配:你存的是字符串,但要匹配的是ObjectId。先通过钩子把提交的字符串转换成ObjectId存入数据库,再选择合适的查询方式即可解决所有问题。
内容的提问来源于stack exchange,提问作者j2logo

