DRF报错:LeadFacilityAssign.assigned_facilities需传入Facility实例而非ID
问题:创建Lead时关联Facility触发ValueError错误
我正尝试创建API端点,通过PUT请求更新已存在的Lead对象并为其添加数量可变的Facility。由于需要为每个关联的Facility绑定时间信息,我创建了LeadFacilityAssign模型。目前先尝试在创建Lead时用POST请求添加单个Facility(听说多Facility需用bulk_create,但未在DRF文档中找到相关内容),但触发了如下错误:
ValueError: Cannot assign "1": "LeadFacilityAssign.assigned_facilities" must be a "Facility" instance.
相关代码
serializers.py
class LeadUpdateSerializer(serializers.ModelSerializer): is_owner = serializers.SerializerMethodField() assigned_facilities = serializers.IntegerField(required=True) datetime = serializers.DateTimeField(required=True) class Meta: model = Lead fields = ( "id", "first_name", "last_name", "assigned_facilities", "datetime", ) read_only_fields = ("id", "created_at", "agent", "is_owner") def get_is_owner(self, obj): user = self.context["request"].user return obj.agent == user def create(self, validated_data): assigned_facilities = validated_data.pop("assigned_facilities") datetime = validated_data.pop("datetime") instance = Lead.objects.create(**validated_data) instance.leadfacility.create(assigned_facilities=assigned_facilities,datetime=datetime) print(instance) return instance
models.py
class Facility(models.Model): name = models.CharField(max_length=150, null=True, blank=False) def __str__(self): return self.name class Lead(models.Model): first_name = models.CharField(max_length=40, null=True, blank=True) last_name = models.CharField(max_length=40, null=True, blank=True) def __str__(self): return f"{self.first_name} {self.last_name}" class LeadFacilityAssign(models.Model): assigned_facilities = models.ForeignKey(Facility, on_delete=models.CASCADE, related_name='leadfacility') lead = models.ForeignKey(Lead, on_delete=models.CASCADE, related_name='leadfacility') datetime = models.DateTimeField()
views.py
class LeadCreateView(CreateAPIView): permission_classes = [IsAuthenticated, IsLeadOwner] serializer_class = LeadUpdateSerializer def perform_create(self, serializer): serializer.save(agent=self.request.user) class LeadUpdateView(UpdateAPIView): permission_classes = [IsAuthenticated, IsLeadOwner] serializer_class = LeadUpdateSerializer def get_queryset(self): return Lead.objects.all()
问题原因及解决方法
核心原因
LeadFacilityAssign的assigned_facilities字段是ForeignKey,要求传入Facility模型实例,但你在Serializer里用IntegerField接收了ID值,并且直接把这个整数传给了该字段,类型不匹配导致报错。
解决方法
方法1:用DRF原生字段自动处理(推荐)
把assigned_facilities字段替换为PrimaryKeyRelatedField,DRF会自动验证ID的合法性并转换为对应实例:
class LeadUpdateSerializer(serializers.ModelSerializer): is_owner = serializers.SerializerMethodField() # 替换IntegerField为PrimaryKeyRelatedField assigned_facilities = serializers.PrimaryKeyRelatedField(queryset=Facility.objects.all(), required=True) datetime = serializers.DateTimeField(required=True) class Meta: model = Lead fields = ( "id", "first_name", "last_name", "assigned_facilities", "datetime", ) read_only_fields = ("id", "created_at", "agent", "is_owner") def get_is_owner(self, obj): user = self.context["request"].user return obj.agent == user def create(self, validated_data): assigned_facilities = validated_data.pop("assigned_facilities") datetime = validated_data.pop("datetime") instance = Lead.objects.create(**validated_data) # 直接传入实例即可 instance.leadfacility.create(assigned_facilities=assigned_facilities, datetime=datetime) return instance
方法2:手动查询实例(兼容原有IntegerField)
如果不想修改字段类型,就在create方法中用ID查询对应的Facility实例:
def create(self, validated_data): facility_id = validated_data.pop("assigned_facilities") datetime = validated_data.pop("datetime") # 验证并获取Facility实例 try: facility = Facility.objects.get(id=facility_id) except Facility.DoesNotExist: raise serializers.ValidationError("指定的Facility不存在") instance = Lead.objects.create(**validated_data) instance.leadfacility.create(assigned_facilities=facility, datetime=datetime) return instance
方法3:扩展支持多Facility批量添加
如果后续需要一次添加多个Facility,只需调整字段为many=True,并使用bulk_create批量创建关联记录:
# Serializer字段修改 assigned_facilities = serializers.PrimaryKeyRelatedField(queryset=Facility.objects.all(), required=True, many=True) # create方法修改 def create(self, validated_data): facilities = validated_data.pop("assigned_facilities") datetime = validated_data.pop("datetime") instance = Lead.objects.create(**validated_data) # 批量生成关联记录 assign_records = [ LeadFacilityAssign(lead=instance, assigned_facilities=fac, datetime=datetime) for fac in facilities ] LeadFacilityAssign.objects.bulk_create(assign_records) return instance
内容的提问来源于stack exchange,提问作者Matthias
相关产品推荐
相关产品推荐

