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

使用FastAPI更新MongoDB时password字段无法更新的问题

问题:MongoDB更新用户时Password字段不生效,其他字段正常

1. MongoDB文档结构

{
  "_id": "8a28d1fc-602b-43ba-a017-105a4fff35b3",
  "isDeleted": false,
  "user": {
    "timestamp": "2023-03-03",
    "phone": "+012345678912",
    "age": 25,
    "gender": "male",
    "nationality": "smth",
    "universityMajor": "ENGINEERING",
    "preferences": {
      "doesMindHavingPets": true
    },
    "password": "$2b$12$9dRbQF2N5kAM44JBAphrrOWT23d206TIZ.VWMhZ9m.PwqnHEZKNhO"
  }
}

2. FastAPI更新函数代码

async def updateUser (id: str, user: BasicUserUpdate = Body(...)):
    new_user = user.dict(exclude_none=True)
    user_preferences = None

    if "preferences" in new_user:
        user_preferences = new_user['preferences']
        del new_user ['preferences']

    if 'password' in new_user:
        new_user['password'] = get_hashed_password(new_user['password'])
        
    print (new_user['password'])
    if (len(new_user) + len(user_preferences)) >= 1:  # type: ignore
        update_result = await dbConnection.find_one_and_update(
        {"_id": id, "isDeleted" : False}, 
        [
            { "$set": 
                { "user.preferences": 
                    { "$mergeObjects": 
                        [ "$user.preferences", user_preferences ] 
                    } 
                }
            },
            { "$set": 
                { "user": 
                    { "$mergeObjects": 
                        [ "$user", new_user ] 
                    } 
                }
            }           
        ], 
        return_document = ReturnDocument.AFTER)

        if update_result is not None:
            return update_result['user']
        else:
            raise HTTPException(status_code=404, detail=f"User {id} not found, or has been banned")

3. 哈希函数实现

password_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

def get_hashed_password(password: str) -> str:
    return password_context.hash(password)

4. 问题现象

  • 所有字段均可正常更新,仅password字段异常:打印显示new_user中的password已正确哈希,但MongoDB中的password始终保持原值。
  • 示例请求体:
{
    "highPrivacy" : "true",
    "password" : "abcd1234",
    "preferences" : {
        "doesMindHavingPets" : "true",
        "isMorningPerson" : "false"
    }
}
  • 打印的new_user内容:
{'password': '$2b$12$7256L8YrH1Yz5/S0GutFM.og1fckAoWUboVqZepMLXZ9KnHk4PwrC', 'highPrivacy': True}

问题原因及修复

原因分析

哈希后的bcrypt密码以$开头,而你使用了MongoDB的聚合式更新(数组形式的update参数)。在$mergeObjects聚合表达式中,MongoDB会将$开头的字符串视为字段引用,而非普通字面量字符串。因此new_user中的哈希密码被错误解析为不存在的字段引用,导致$mergeObjects合并时无法覆盖原password字段。

修复方案

将聚合式的整个user对象合并操作,改为逐个字段设置的普通$set操作,这样MongoDB会将哈希密码当作普通字符串处理,避免解析错误。

修改后的更新函数代码:

async def updateUser(id: str, user: BasicUserUpdate = Body(...)):
    new_user = user.dict(exclude_none=True)
    user_preferences = None

    if "preferences" in new_user:
        user_preferences = new_user['preferences']
        del new_user['preferences']

    if 'password' in new_user:
        new_user['password'] = get_hashed_password(new_user['password'])
        
    print(new_user['password'])
    update_ops = []
    
    # 处理user下的普通字段(包括password)
    if new_user:
        update_ops.append({
            "$set": {f"user.{key}": value for key, value in new_user.items()}
        })
    
    # 处理preferences的合并
    if user_preferences:
        update_ops.append({
            "$set": {
                "user.preferences": {
                    "$mergeObjects": ["$user.preferences", user_preferences]
                }
            }
        })
    
    if update_ops:
        update_result = await dbConnection.find_one_and_update(
            {"_id": id, "isDeleted": False},
            update_ops,
            return_document=ReturnDocument.AFTER
        )

        if update_result is not None:
            return update_result['user']
        else:
            raise HTTPException(status_code=404, detail=f"User {id} not found, or has been banned")

关键修改点

  1. 取消了原有的$mergeObjects合并整个user对象的操作,改为遍历new_user的键值对,逐个设置user.{key}字段。
  2. 保留了user.preferences的$mergeObjects合并逻辑,因为这部分需要合并原有偏好和新偏好,且偏好值不会出现$开头的情况。

这样修改后,哈希密码会被当作普通字符串直接写入MongoDB,不会被错误解析为字段引用,password字段即可正常更新。


内容的提问来源于stack exchange,提问作者Ahmet-Salman

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 22:37:03