Django rest framework 的嵌套序列化器创建方法。
Django rest framework 的嵌套序列化器创建方法。
我创建了一个嵌套的序列化程序,当我试图在其中发布数据时,它仍然显示外键值不能为空或需要字典的错误。我已经查阅了各种类似的问题并尝试了不同的响应,但对我来说不起作用。这是模型
##CLasses class Classes(models.Model): class_name = models.CharField(max_length=255) class_code = models.CharField(max_length=255) created_date = models.DateTimeField(auto_now_add=True) def __str__(self): return self.class_name class Meta: ordering = ['class_code'] ##Streams class Stream(models.Model): stream_name = models.CharField(max_length=255) classes = models.ForeignKey(Classes,related_name="classes",on_delete=models.CASCADE) created_date = models.DateTimeField(auto_now_add=True) def __str__(self): return self.stream_name class Meta: ordering = ['stream_name']
这里是视图
class StreamViewset(viewsets.ModelViewSet): queryset = Stream.objects.all() serializer_class = StreamSerializer
这是序列化类
class StreamSerializer(serializers.ModelSerializer): # classesDetails = serializers.SerializerMethodField() classes = ClassSerializer() class Meta: model = Stream fields = '__all__' def create(self,validated_data): classes = Classes.objects.get(id=validated_data["classes"]) return Stream.objects.create(**validated_data, classes=classes) # def perfom_create(self,serializer): # serializer.save(classes=self.request.classes) #depth = 1 # def get_classesDetails(self, obj): # clas = Classes.objects.get(id=obj.classes) # classesDetails = ClassSerializer(clas).data # return classesDetails
我尝试了几种启用create方法的方法,但是像这样显示错误{\"classes\":{\"non_field_errors\":[\"Invalid data. Expected a dictionary, but got int.\"]}}
。任何贡献都将不胜感激
Kevin Languasco非常好地描述了create
方法的行为,他的解决方案很有效。我会添加一个解决方案1的变化:
class StreamSerializer(serializers.ModelSerializer): classes = ClassSerializer(read_only=True) classes_id = serializers.IntegerField(write_only=True) def create(self,validated_data): return Stream.objects.create(**validated_data, classes=classes) class Meta: model = Stream fields = ( 'pk', 'stream_name', 'classes', 'classes_id', 'created_date', )
如果需要的话,您可以像您的示例那样覆盖create
方法,但是序列化程序可以在不覆盖create
方法的情况下工作。
将classes_id
的值传递到POST方法的主体中,而不是classes
。在反序列化数据时,验证将跳过classes
并转而检查classes_id
。
在序列化数据时(例如执行GET请求时),classes
将与嵌套的字典一起使用,而classes_id
将被省略。
当使用DRF开发API时,这是一个非常普遍的情况。
问题
在DRF到达create()
方法之前,它会验证输入,我假设其表单类似于
{ "classes": 3, "stream_name": "example" }
这意味着,由于指定了
classes = ClassSerializer()
DRF正试图从整数构建classes
字典。 当然,这将失败,并且您可以从错误字典中看到
{"classes":{"non_field_errors":["Invalid data. Expected a dictionary, but got int."]}}
解决方案1(需要新的可写字段{field_name}_id
)
一种可能的解决方案是在您的ClassSerializer
中设置read_only=True
,并在编写时使用替代名称字段,通常使用{field_name}_id
。 这样,就不会进行验证。 关于更多详细信息,请参见此答案。
class StreamSerializer(serializers.ModelSerializer): classes = ClassSerializer(read_only=True) class Meta: model = Stream fields = ( 'pk', 'stream_name', 'classes', 'created_date', 'classes_id', ) extra_kwargs = { 'classes_id': {'source': 'classes', 'write_only': True}, }
这是一个干净的解决方案,但需要更改用户API。 如果这不是选择,请继续下一个解决方案。
解决方案2(需要覆盖to_internal_value
)
这里我们覆盖to_internal_value
方法。 这是嵌套的ClassSerializer
引发错误的地方。 为了避免这种情况,我们将该字段设置为read_only
并在该方法中管理验证和解析。
请注意,由于我们在可写表示中未声明classes
字段,super()。to_internal_value
的默认操作是忽略字典中的值。
from rest_framework.exceptions import ValidationError class StreamSerializer(serializers.ModelSerializer): classes = ClassSerializer(read_only=True) def to_internal_value(self, data): classes_pk = data.get('classes') internal_data = super().to_internal_value(data) try: classes = Classes.objects.get(pk=classes_pk) except Classes.DoesNotExist: raise ValidationError( {'classes': ['Invalid classes primary key']}, code='invalid', ) internal_data['classes'] = classes return internal_data class Meta: model = Stream fields = ( 'pk', 'stream_name', 'classes', 'created_date', )
有了这个解决方案,您可以在读取和写入时使用相同的字段名称,但代码有点凌乱。
附加注释
- 您正在错误地使用
related_name
参数,请参见此问题。 情况正好相反,
classes = models.ForeignKey( Classes, related_name='streams', on_delete=models.CASCADE, )
在这种情况下,应该是streams
。