Django中update_or_create指定kwargs仍触发唯一约束错误的解决
问题描述
我有如下Django模型:
class Itens(models.Model): id = models.AutoField(primary_key=True) itempr_id = models.IntegerField(unique=True) # This is NOT a relationship cod_item = models.CharField(max_length=16, unique=True) # Other fields...
因为itempr_id和cod_item都是唯一字段,我在序列化器里用update_or_create来处理数据:
class CreateItensSerializer(serializers.ModelSerializer): class Meta: model = Itens fields = '__all__' def create(self, validated_data): item, created = Itens.objects.update_or_create( itempr_id=validated_data.get('itempr_id'), cod_item=validated_data.get('cod_item'), defaults=validated_data ) return item
调用序列化器保存数据的代码如下:
serializer = CreateItensSerializer(data=itens, many=True) if serializer.is_valid(): serializer.save()
但当数据存在重复时,会收到如下错误:
{'itempr_id': [ErrorDetail(string='itens with this itempr id already exists.', code='unique')], 'cod_item': [ErrorDetail(string='itens with this cod item already exists.', code='unique')]}
我以为已经通过update_or_create处理了唯一字段的冲突,不该出现这个问题,该怎么解决?
编辑说明:数据库为空时代码正常运行,传入的itens参数没有问题。
解决方案
问题出在Django REST Framework的序列化器验证阶段——在调用save()执行你自定义的create方法之前,序列化器已经先做了模型字段的唯一性校验,发现数据库里已有对应值,直接抛出了错误,根本没走到你写的update_or_create逻辑。
解决方法分两种,按需选择:
方法一:覆盖字段级验证逻辑,跳过默认唯一校验
在序列化器里为itempr_id和cod_item单独重写字段验证方法,跳过默认的唯一性检查:
class CreateItensSerializer(serializers.ModelSerializer): class Meta: model = Itens fields = '__all__' def validate_itempr_id(self, value): # 跳过默认唯一校验,交给update_or_create处理 return value def validate_cod_item(self, value): # 跳过默认唯一校验,交给update_or_create处理 return value def create(self, validated_data): item, created = Itens.objects.update_or_create( itempr_id=validated_data.get('itempr_id'), cod_item=validated_data.get('cod_item'), defaults=validated_data ) return item
方法二:手动关闭序列化器字段的唯一约束
在序列化器中重新定义字段,将unique参数设为False,让序列化器不自动执行唯一性验证:
class CreateItensSerializer(serializers.ModelSerializer): itempr_id = serializers.IntegerField(unique=False) cod_item = serializers.CharField(max_length=16, unique=False) class Meta: model = Itens fields = '__all__' def create(self, validated_data): item, created = Itens.objects.update_or_create( itempr_id=validated_data.get('itempr_id'), cod_item=validated_data.get('cod_item'), defaults=validated_data ) return item
注意事项
- 两种方法都是让序列化器跳过提前校验,把唯一字段的判断逻辑交给
update_or_create,既解决了校验错误问题,又能实现更新或创建的需求。 - 数据库层面的唯一约束依然生效,不用担心出现脏数据。
内容的提问来源于stack exchange,提问作者Akari Oozora
相关产品推荐
相关产品推荐

