首页
/ Tortoise-ORM中update_or_create方法的KeyError问题解析

Tortoise-ORM中update_or_create方法的KeyError问题解析

2025-06-09 01:27:16作者:管翌锬

问题背景

在使用Tortoise-ORM进行数据库操作时,开发者可能会遇到一个关于update_or_create方法的异常问题。这个问题表现为当尝试更新或创建一个模型实例时,系统抛出KeyError异常,提示缺少'id'字段。

问题复现

让我们通过一个具体的代码示例来重现这个问题:

class Faqs(BaseModel):
    id = fields.UUIDField(pk=True)
    question = fields.TextField(null=True)

async def test_case():
    # 假设数据库中已存在该记录
    faq = await Faqs.get(id="6aadafff-e0b9-497b-aeb2-65498a35b6f5")
    
    # 修改问题内容
    faq.question = "q111234"
    
    # 分配新的ID
    faq.id = uuid.uuid4()
    
    # 尝试更新或创建,此时会抛出KeyError
    new_faq = await Faqs.update_or_create(id=faq.id, defaults=dict(faq))

问题分析

这个问题的根源在于Python字典合并的行为差异以及Tortoise-ORM内部实现的方式。具体来说:

  1. 当调用dict(faq)时,会创建一个包含模型所有字段的字典
  2. 这个字典已经包含了'id'字段
  3. 在Python 3.12之前的版本中,当尝试合并两个包含相同键的字典时,行为是不一致的
  4. Tortoise-ORM的update_or_create方法内部会尝试合并defaultskwargs参数

技术细节

在Python 3.12之前,字典合并操作{**d1, **d2}当遇到重复键时,行为是不确定的。在某些情况下会抛出KeyError,而在另一些情况下会正常覆盖值。这正是导致我们遇到KeyError的根本原因。

Tortoise-ORM的update_or_create方法实际上是调用了get_or_create方法,而后者又会将defaultskwargs合并后传递给create方法。当两个字典都包含'id'字段时,就会触发这个问题。

解决方案

针对这个问题,有以下几种解决方案:

  1. 显式处理字段:在创建defaults字典时,明确指定需要的字段,避免包含id字段
new_faq = await Faqs.update_or_create(
    id=faq.id, 
    defaults={"question": faq.question}
)
  1. 使用Python 3.12+:Python 3.12及更高版本已经修复了字典合并的行为不一致问题

  2. 手动创建或更新:可以分步骤先尝试获取,不存在时再创建

try:
    new_faq = await Faqs.get(id=faq.id)
    await new_faq.update_from_dict({"question": faq.question})
    await new_faq.save()
except DoesNotExist:
    new_faq = await Faqs.create(id=faq.id, question=faq.question)

最佳实践

为了避免这类问题,在使用Tortoise-ORM时建议:

  1. 明确指定需要更新的字段,而不是直接转换整个模型实例为字典
  2. 对于复杂操作,考虑使用事务保证数据一致性
  3. 在团队开发中统一Python版本,避免因版本差异导致的行为不一致
  4. 对于关键业务逻辑,实现自定义的创建/更新方法,而不是完全依赖ORM的快捷方法

总结

Tortoise-ORM中的update_or_create方法在使用时需要注意字典合并的行为特性,特别是在处理主键字段时。理解ORM内部的工作原理和Python语言特性的交互,可以帮助开发者编写更健壮的数据库操作代码。通过采用明确的字段指定策略或升级Python版本,可以有效避免这类KeyError问题的发生。

登录后查看全文
热门项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
22
6
docsdocs
OpenHarmony documentation | OpenHarmony开发者文档
Dockerfile
153
1.98 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
505
42
nop-entropynop-entropy
Nop Platform 2.0是基于可逆计算理论实现的采用面向语言编程范式的新一代低代码开发平台,包含基于全新原理从零开始研发的GraphQL引擎、ORM引擎、工作流引擎、报表引擎、规则引擎、批处理引引擎等完整设计。nop-entropy是它的后端部分,采用java语言实现,可选择集成Spring框架或者Quarkus框架。中小企业可以免费商用
Java
8
0
ohos_react_nativeohos_react_native
React Native鸿蒙化仓库
C++
194
279
openHiTLSopenHiTLS
旨在打造算法先进、性能卓越、高效敏捷、安全可靠的密码套件,通过轻量级、可剪裁的软件技术架构满足各行业不同场景的多样化要求,让密码技术应用更简单,同时探索后量子等先进算法创新实践,构建密码前沿技术底座!
C
992
395
RuoYi-Vue3RuoYi-Vue3
🎉 (RuoYi)官方仓库 基于SpringBoot,Spring Security,JWT,Vue3 & Vite、Element Plus 的前后端分离权限管理系统
Vue
938
554
communitycommunity
本项目是CANN开源社区的核心管理仓库,包含社区的治理章程、治理组织、通用操作指引及流程规范等基础信息
333
11
openGauss-serveropenGauss-server
openGauss kernel ~ openGauss is an open source relational database management system
C++
146
191
金融AI编程实战金融AI编程实战
为非计算机科班出身 (例如财经类高校金融学院) 同学量身定制,新手友好,让学生以亲身实践开源开发的方式,学会使用计算机自动化自己的科研/创新工作。案例以量化投资为主线,涉及 Bash、Python、SQL、BI、AI 等全技术栈,培养面向未来的数智化人才 (如数据工程师、数据分析师、数据科学家、数据决策者、量化投资人)。
Python
75
70