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

PyMongo(Motor)报DuplicateKeyError且try-except不生效如何处理

Motor异步写入MongoDB时DuplicateKeyError无法捕获的解决方法

问题根因

现有代码的异常捕获不生效,核心是两个写法错误:

  • Motor作为异步MongoDB驱动,所有数据读写方法返回协程对象,必须加await关键字才会实际执行。当前add_user是同步函数,调用insert_one时仅发起了操作请求,没有等待操作执行完成就退出了try块,等实际写入触发重复键错误时,已经不在异常捕获的作用域内。
  • 未正确等待异步协程执行,导致异常抛出时没有对应的捕获逻辑承接。

修复方案

方案1:修正异步逻辑+显式捕获重复键异常

首先导入重复键对应的异常类,将数据库操作函数改为异步函数,调用时增加await等待执行完成,再针对性捕获异常即可。
完整修正代码:

import motor.motor_asyncio
from datetime import datetime
from pymongo.errors import DuplicateKeyError
from aiogram import Bot, types
from aiogram.dispatcher import Dispatcher
from aiogram.utils import executor

TOKEN = ''
cluster = motor.motor_asyncio.AsyncIOMotorClient('')
collection = cluster.Dimalexus.BOT

bot = Bot(token=TOKEN)
dp = Dispatcher(bot)

async def add_user(user_id, user_name, name):
    date = datetime.now().date()
    await collection.insert_one({
        '_id' : user_id,
        'name' : name,
        "username" : user_name,
        "date" : str(date)
    })

@dp.message_handler(commands=['start'])
async def welcome_send_info(message: types.Message):
    await message.reply('привет')
    name = message.from_user.full_name
    user_name = message.from_user.username
    user_id = message.from_user.id
    try:
        await add_user(user_id, user_name, name)
    except DuplicateKeyError:
        # 用户已存在,跳过重复插入逻辑
        pass
    except Exception as e:
        print(f"用户数据写入异常: {e}")

if __name__ == "__main__":
    executor.start_polling(dp, skip_updates=True)

方案2:使用upsert原子操作规避重复键错误(推荐)

不需要依赖异常捕获处理重复用户场景,直接使用update_one搭配upsert=True实现原子写入:如果用户_id不存在就插入新记录,如果已存在就更新用户最新的昵称、用户名信息,从根源上避免重复键报错,逻辑更健壮、性能更好。
对应修改add_user函数即可:

async def add_user(user_id, user_name, name):
    date = datetime.now().date()
    await collection.update_one(
        filter={"_id": user_id},
        update={
            "$setOnInsert": {"date": str(date)},
            "$set": {"name": name, "username": user_name}
        },
        upsert=True
    )

其中$setOnInsert修饰的字段只会在首次插入新用户时写入,不会覆盖用户原有的注册日期;$set修饰的字段每次用户触发/start时都会同步更新,能自动适配用户修改昵称、用户名的场景。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 06:18:21