如何处理异步POST场景的django.db.utils.IntegrityError?可否捕获该错误?
问题根因
你遇到的是典型的应用层查重竞态问题:BEFORE_CREATE钩子内的slug存在性校验是在Django应用层执行的,并发请求下两个事务会同时完成查重判断,都认为["hello"]不存在,随后同时写入触发唯一约束冲突。
另外你原代码里还有两个语法问题可以顺便修正:一是populate_slug方法内的slug变量没有初始化,二是拼接后缀的语法错误f"{slug[0]-{n}"应为f"{slug[0]}-{n}"。
方案1:捕获IntegrityError重试(推荐,符合你的需求)
不需要把钩子改成AFTER_CREATE,只要在创建逻辑的外层加冲突捕获重试即可,只有出现冲突的时候才会重试写入,正常场景下不会产生额外的数据库操作,你可以把这个逻辑封装为独立的创建方法,或者放在视图层:
from django.db import IntegrityError, transaction from django.utils.text import slugify def create_item(title: str) -> Item: base_slug = slugify(title) # 加重试上限避免极端场景下无限循环 max_retry = 100 retry_count = 0 while retry_count < max_retry: try: # 原子事务保证创建失败时回滚脏数据 with transaction.atomic(): if retry_count == 0: current_slug = [base_slug] else: current_slug = [f"{base_slug}-{retry_count + 1}"] return Item.objects.create(title=title, slug=current_slug) except IntegrityError as e: # 只捕获slug唯一约束冲突的错误,其他错误正常抛出 if "app_item_slug_key" not in str(e): raise retry_count += 1 raise RuntimeError("slug生成重试次数超限")
后续创建Item时直接调用create_item方法即可,无需再依赖原来的BEFORE_CREATE钩子,也可以把这段逻辑整合到钩子内部。
其他可选方案
- 如果你并发量极高,可以把slug生成逻辑放到PostgreSQL的触发器中实现,完全在数据库层面保证操作的原子性,缺点是业务逻辑散落在数据库中,维护成本更高。
- 也可以使用PostgreSQL的咨询锁(Advisory Lock)在创建前加全局锁,避免并发冲突,但锁的粒度更粗,高并发下性能会受影响。
内容的提问来源于stack exchange,提问作者Chris
相关产品推荐
相关产品推荐

