Python Eve:通过事件钩子在auth_field中添加_id值遇KeyError问题
解决注册端点添加AUTH_FIELD的KeyError问题及优化方案
首先,你遇到的KeyError: '_id'问题原因很清晰:前端发送的POST请求payload里根本没有_id字段。_id是MongoDB在文档插入时自动生成的,不会出现在用户提交的请求数据里,所以你从data['_id']取值自然会报错。
接下来我给你两种解决方案,从修复现有问题到更简便的实现方式:
一、修复钩子逻辑:使用插入后钩子获取自动生成的_id
你之前用的on_post_POST_accounts钩子是在请求响应发送后触发的,而且只能拿到原始请求数据,拿不到MongoDB生成的_id。正确的做法是使用框架的on_inserted_<resource>钩子——这个钩子会在文档成功插入数据库后触发,此时每个文档已经包含自动生成的_id。
修改后的代码如下:
from bson.objectid import ObjectId AUTH_FIELD = 'user_id' def add_user_id_after_insert(items): for item in items: # 从插入后的文档中获取MongoDB自动生成的_id document_id = item['_id'] # 将user_id设置为_id的字符串形式(或直接用ObjectId类型,看你的需求) app.data.driver.db['accounts'].update_one( {'_id': document_id}, {'$set': {AUTH_FIELD: str(document_id)}} ) # 绑定到accounts集合的插入后钩子 app.on_inserted_accounts += add_user_id_after_insert
二、更简便的实现:直接在Schema中配置默认值(或复用_id)
如果你只是需要user_id字段与文档的_id保持一致,其实可以不用钩子,直接在DOMAIN配置中做文章:
方案1:设置user_id为_id的别名(无需额外存储)
如果你的需求只是在响应中返回user_id(而不是必须在集合中存储这个字段),可以在Schema中配置user_id为_id的只读映射:
DOMAIN = { 'accounts': { 'schema': { 'username': {'type': 'string', 'required': True}, # 其他字段... 'user_id': { 'type': 'objectid', 'readonly': True, 'default': lambda doc: doc['_id'] if '_id' in doc else None } }, # 确保响应中返回user_id字段 'response_fields': ['username', 'user_id'] } }
方案2:自动生成user_id并存储
如果必须在集合中存储user_id字段,也可以在Schema中设置默认值,让系统自动生成独立的唯一值:
from bson.objectid import ObjectId DOMAIN = { 'accounts': { 'schema': { 'username': {'type': 'string', 'required': True}, # 其他字段... 'user_id': { 'type': 'string', 'required': True, 'unique': True, 'default': lambda: str(ObjectId()) } } } }
这种方式下,用户注册时无需提交user_id,系统会自动生成并存储,完全不需要钩子。
补充说明
- 如果你使用的不是Eve框架,而是Flask-PyMongo之类的,逻辑类似:不要从请求数据中拿
_id,而是在文档插入后获取生成的_id再更新。 - 尽量避免在
on_post_POST_*钩子中操作数据库,因为此时响应已经发送给用户,数据库操作失败的话用户无法感知。
内容的提问来源于stack exchange,提问作者scenox
相关产品推荐
相关产品推荐

