Django Rest框架嵌套序列化器创建方法

And*_*rew 4 python django 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']
Run Code Online (Sandbox Code Playgroud)

这是视图

class StreamViewset(viewsets.ModelViewSet):
    queryset = Stream.objects.all()
    serializer_class = StreamSerializer
Run Code Online (Sandbox Code Playgroud)

这是序列化器类

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

Run Code Online (Sandbox Code Playgroud)

我尝试了多种启用创建方法的方法,但像这样显示错误{"classes":{"non_field_errors":["Invalid data. Expected a dictionary, but got int."]}}。任何贡献将不胜感激

Kev*_*sco 9

这是使用 DRF 开发 API 时非常常见的情况。

问题

在 DRF 到达该create()方法之前,它会验证输入,我认为其形式类似于

{
   "classes": 3,
   "stream_name": "example"
}
Run Code Online (Sandbox Code Playgroud)

这意味着,既然指定了

{
   "classes": 3,
   "stream_name": "example"
}
Run Code Online (Sandbox Code Playgroud)

classesDRF 正在尝试从整数构建字典。当然,这样会失败,从错误字典中可以看到

classes = ClassSerializer()
Run Code Online (Sandbox Code Playgroud)

解决方案1(需要一个新的可写字段{field_name}_id

read_only=True一个可能的解决方案是在您的 中设置ClassSerializer,并在编写时使用该字段的替代名称,这很常见{field_name}_id。这样,验证就不会完成。有关更多详细信息,请参阅此答案。

{"classes":{"non_field_errors":["Invalid data. Expected a dictionary, but got int."]}}
Run Code Online (Sandbox Code Playgroud)

这是一个干净的解决方案,但需要更改用户 API。如果这不是一个选项,请继续执行下一个解决方案。

解决方案2(需要覆盖to_internal_value

这里我们重写该to_internal_value方法。这是嵌套ClassSerializer抛出错误的地方。为了避免这种情况,我们将该字段设置为read_only并管理方法中的验证和解析。

请注意,由于我们没有classes在可写表示中声明字段,因此默认操作super().to_internal_value是忽略字典中的值。

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},
    }
Run Code Online (Sandbox Code Playgroud)

通过这个解决方案,您可以使用相同的字段名称进行读取和写入,但代码有点混乱。

补充笔记

  • related_name错误地使用了该参数,请参阅此问题。恰恰相反,
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',
    )
Run Code Online (Sandbox Code Playgroud)

在这种情况下应该是streams