Django的get_or_create返回False或抛出IntegrityError的原因是什么
get_or_create两种返回结果的根本原因
- 当
get_or_create执行第一阶段的SELECT查询,用你传入的所有非defaults字段作为筛选条件,成功匹配到数据库中已有的记录时,会直接返回(匹配到的实例, False),不会执行后续创建逻辑,这就是你遇到的created=False的场景,属于正常的查询命中逻辑。 - 当第一阶段SELECT查询没有匹配到记录,进入INSERT创建阶段时,写入操作触发了数据库层面的唯一约束校验,就会抛出
IntegrityError,你的场景下触发的原因有两个:- 查询参数与唯一约束不匹配:你的模型设置的联合唯一约束是
account_name、date、target三个字段,但你调用get_or_create时,在非defaults参数里额外加了user作为查询条件。如果数据库中已经存在和你传入的account_name、date、target一致的记录,但这条记录的user字段值和你传入的user不同,第一阶段SELECT用四个字段联合查询就会查不到数据,进入INSERT阶段时就会触发唯一约束报错。 - 并发竞争(Race Condition):就算你把查询参数调整为和唯一约束完全匹配,在高并发场景下依然可能触发报错。因为
get_or_create的查询和写入不是数据库层面的原子操作,两个请求同时执行get_or_create时,可能都在第一阶段SELECT查询不到记录,同时进入INSERT阶段,第一个请求写入成功,第二个请求写入时就会触发唯一约束校验失败,Django默认不会在写入失败后自动重试查询,会直接抛出异常。
- 查询参数与唯一约束不匹配:你的模型设置的联合唯一约束是
修复建议
- 调整
get_or_create的传参,把不属于唯一约束的user字段放到defaults中:
action_history, action_added = ActionModel.objects.get_or_create( date=date_obj, account_name=unique_name, target=follower_obj, defaults={ 'identifier': history['user_id'], 'user': user } )
- 增加异常捕获处理兼容并发场景:
from django.db import IntegrityError try: action_history, action_added = ActionModel.objects.get_or_create( date=date_obj, account_name=unique_name, target=follower_obj, defaults={ 'identifier': history['user_id'], 'user': user } ) except IntegrityError: # 并发写入冲突,重新查询已存在的记录 action_history = ActionModel.objects.get( date=date_obj, account_name=unique_name, target=follower_obj, ) action_added = False
内容的提问来源于stack exchange,提问作者flice com
相关产品推荐
相关产品推荐

