Pymongo插入与查询引用问题:跨表引用插入报$ref格式错误
解决MongoDB插入引用时的bson错误问题
嘿,我来帮你搞定这个问题!你遇到的bson.errors.InvalidDocument: key '$ref' must not start with '$'错误,主要是因为手动构造DBRef的方式不对,而且忽略了MongoDB驱动对引用格式的要求。
错误原因分析
你写的插入语句:
self.db.insert('admin', {'char_id': {'$ref' : 'player', 'char_id': char_id}})
有两个关键问题:
- DBRef的结构不正确:MongoDB的DBRef必须包含
$ref(集合名)和$id(引用文档的主键值)这两个必填字段,你用了自定义的char_id键代替了$id,这不符合规范。 - 驱动不认可手动构造的$开头键:虽然
$ref是MongoDB的特殊键,但直接用字典构造时,部分驱动会把它当成普通的自定义键(而自定义键是不允许以$开头的),所以触发了错误。
正确的解决方法
方法1:使用官方DBRef类构造引用(推荐)
大部分MongoDB驱动(比如pymongo)都提供了专门的DBRef类来创建合法的引用,这样能避免手动构造的格式问题。步骤如下:
- 先导入DBRef类
- 找到要引用的player文档的主键(默认是
_id,如果你用char_id作为主键的话可以直接用它) - 构造DBRef对象并插入到admin集合中
示例代码:
from bson.dbref import DBRef # 先获取要引用的player文档(假设char_id是player集合中的字段) target_player = self.db.player.find_one({'char_id': char_id}) if target_player: # 构造DBRef:第一个参数是集合名,第二个是引用文档的主键 player_ref = DBRef('player', target_player['_id']) # 插入到admin集合 self.db.admin.insert_one({ 'char_id': char_info.char_id, 'name': char_info.name, 'player_ref': player_ref # 存储引用 })
方法2:手动存储关联字段(更简单,适合同库关联)
如果你的两个集合在同一个数据库里,其实不需要用DBRef,直接存储char_id这个关联字段,查询的时候手动关联两个集合就行,这种方式更直观且性能更好:
# 插入admin文档时直接存char_id self.db.admin.insert_one({ 'char_id': char_info.char_id, 'name': char_info.name, 'player_char_id': char_id # 存储关联的player char_id }) # 查询时手动关联player集合 admin_docs = self.db.admin.find() for admin_doc in admin_docs: # 根据存储的player_char_id查询对应的player文档 player_doc = self.db.player.find_one({'char_id': admin_doc['player_char_id']}) # 处理关联后的完整数据
额外提醒
- 如果你确实想用
char_id作为DBRef的引用标识,那需要确保player集合的主键就是char_id(而不是默认的_id),否则DBRef无法正确找到对应的文档。 - DBRef主要适用于跨数据库的引用场景,同库内的关联用手动存储关联字段的方式会更高效。
内容的提问来源于stack exchange,提问作者Trax
相关产品推荐
相关产品推荐

