You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 10:41:18