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方法:
- DRF的嵌套序列化器(如
ProviderSerializer)默认行为是验证并创建新的关联对象。由于Provider的name是主键(自带唯一性约束),当提交已存在的name时,序列化器会触发唯一性验证,直接抛出错误,不会进入create方法中的get_or_create逻辑。 - 你看到的"逻辑一致"只是
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
相关产品推荐
相关产品推荐

