Django更新用户触发authtoken_token.user_id唯一约束错误如何解决?
错误产生原因
- 核心错误来自序列化器
update方法中的令牌更新语句Token.objects.update(user=instance):Django ORM中QuerySet.update()是批量操作,你没有添加过滤条件,这段代码会尝试把数据库中所有令牌的user_id字段都修改为当前操作用户的ID,而authtoken_token表的user_id字段自带唯一约束,不允许多条令牌记录绑定同一个用户ID,因此直接触发唯一约束冲突报错。 - 额外隐性问题:你导入
Token类的路径错误,正确导入路径为rest_framework.authtoken.models.Token,当前从views导入的写法本身就不符合规范,只是本次报错不是该问题触发。 - 补充说明:就算没有触发唯一约束,这个写法也会把全量用户的令牌都绑定到当前操作用户,属于严重的逻辑漏洞。
修复方案
根据业务需求可选择两种修复方案:
场景1:修改用户信息不需要刷新登录令牌
绝大多数场景下,修改用户名、密码不需要作废用户当前的登录令牌,直接删除update方法中错误的令牌更新语句即可,修改后代码如下:
def update(self, instance, validated_data): instance.username = validated_data['username'] instance.set_password(validated_data['password']) instance.save() # 直接删除原有的Token.objects.update行即可 return instance
场景2:修改用户信息后需要作废旧令牌、生成新令牌
如果你的业务逻辑要求修改密码后旧登录令牌失效,需要生成新令牌,先修正Token的导入路径,再调整update方法的逻辑即可:
# 修正Token导入路径 from rest_framework.authtoken.models import Token # 修改update方法 def update(self, instance, validated_data): instance.username = validated_data['username'] instance.set_password(validated_data['password']) instance.save() # 先删除当前用户的旧令牌 Token.objects.filter(user=instance).delete() # 生成新令牌 new_token = Token.objects.create(user=instance) # 如果需要返回新令牌给前端,可将new_token.key加到序列化返回字段中 return instance
内容的提问来源于stack exchange,提问作者Himanshu Poddar
相关产品推荐
相关产品推荐

