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

Django序列化器get_or_create异常:Provider重复报错原因排查

问题分析与解决

背景代码

模型定义

class Article(AbstractBaseModel):
    manufacturer = models.ForeignKey(
        Manufacturer,
        on_delete=models.CASCADE,
        null=True,
        blank=True
    )

    provider1 = models.ForeignKey(
        Provider,
        on_delete=models.CASCADE,
        null=True,
        blank=True,
    )
    
    def get_absolute_url(self):
        return reverse(
            "smt_management_app:article-detail", kwargs={"name": self.name}
        )
class Manufacturer(models.Model):
    name = models.CharField(
        primary_key=True, max_length=50, null=False, blank=False
    )

    def get_absolute_url(self):
        return reverse(
            "smt_management_app:manufacturer-detail", kwargs={"name": self.name}
        )
class Provider(models.Model):
    name = models.CharField(
        primary_key=True, max_length=50, null=False, blank=False
    )

    def get_absolute_url(self):
        return reverse("smt_management_app:provider-detail", kwargs={"name": self.name})

序列化器与视图集

class ArticleSerializer(serializers.ModelSerializer):
    manufacturer = ManufacturerSerializer(required=False, allow_null=True)
    provider1 = ProviderSerializer(required=False, allow_null=True)

    class Meta:
        model = Article
        fields = [
            "name",
            "manufacturer",
            "provider1",
        ]

    def create(self, validated_data):
        manufacturer_data = validated_data.pop("manufacturer", None)
        if manufacturer_data:
            manufacturer, _ = Manufacturer.objects.get_or_create(
                name=manufacturer_data["name"]
            )
            validated_data["manufacturer"] = manufacturer

        provider1_data = validated_data.pop("provider1", None)
        if provider1_data:
            provider1, _= Provider.objects.get_or_create(
                name=provider1_data["name"]
            )
            validated_data["provider1"] = provider1

        article = Article.objects.create(**validated_data)
        return article
class ArticleViewSet(viewsets.ModelViewSet):
    queryset = Article.objects.all()
    serializer_class = ArticleSerializer
    filter_backends = (
        django_filters.rest_framework.DjangoFilterBackend,
        filters.SearchFilter,
        filters.OrderingFilter,
    )
    filterset_class = ArticleFilter
    ordering_fields = "__all__"

问题现象

提交POST请求创建新Article时,已知Manufacturer(name="123")和Provider(name="abc")均已存在,但仅Provider触发错误:
请求体:

{"name":"article1","manufacturer":{"name":"123"},"provider1":{"name":"abc"}}

错误信息:

{"provider1":{"name":["provider with this name already exists."]}}

原因分析

问题出在嵌套序列化器的验证阶段,而非create方法:

  1. DRF的嵌套序列化器(如ProviderSerializer)默认行为是验证并创建新的关联对象。由于Provider的name是主键(自带唯一性约束),当提交已存在的name时,序列化器会触发唯一性验证,直接抛出错误,不会进入create方法中的get_or_create逻辑。
  2. 你看到的"逻辑一致"只是ArticleSerializer中的代码,但ManufacturerSerializer和ProviderSerializer的实际定义存在差异——比如ManufacturerSerializer可能被设置了name字段只读、自定义了验证逻辑跳过唯一性检查,或是其他修改,导致它没有触发主键唯一性验证。

解决办法

你的业务意图是关联已存在的对象,不存在则自动创建,嵌套序列化器的默认行为不符合这个需求,建议改用SlugRelatedField:

方案1:直接关联已存在对象

如果只需要关联已存在的Manufacturer/Provider,不需要自动创建:

class ArticleSerializer(serializers.ModelSerializer):
    manufacturer = serializers.SlugRelatedField(
        queryset=Manufacturer.objects.all(),
        slug_field='name',
        required=False,
        allow_null=True
    )
    provider1 = serializers.SlugRelatedField(
        queryset=Provider.objects.all(),
        slug_field='name',
        required=False,
        allow_null=True
    )

    class Meta:
        model = Article
        fields = ["name", "manufacturer", "provider1"]

    def create(self, validated_data):
        article = Article.objects.create(**validated_data)
        return article

此时请求体可以简化为:

{"name":"article1","manufacturer":"123","provider1":"abc"}

方案2:自动创建不存在的对象

如果需要自动创建不存在的Manufacturer/Provider,可以自定义字段:

class CreateSlugRelatedField(serializers.SlugRelatedField):
    def to_internal_value(self, data):
        try:
            return self.get_queryset().get(**{self.slug_field: data})
        except self.queryset.model.DoesNotExist:
            return self.queryset.model.objects.create(**{self.slug_field: data})

class ArticleSerializer(serializers.ModelSerializer):
    manufacturer = CreateSlugRelatedField(
        queryset=Manufacturer.objects.all(),
        slug_field='name',
        required=False,
        allow_null=True
    )
    provider1 = CreateSlugRelatedField(
        queryset=Provider.objects.all(),
        slug_field='name',
        required=False,
        allow_null=True
    )

    class Meta:
        model = Article
        fields = ["name", "manufacturer", "provider1"]

内容的提问来源于stack exchange,提问作者Astrionn

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 15:07:33