编写Django Rest序列化器的问题

时间:2019-08-30 13:29:11

标签: python django serialization django-rest-framework

我正在尝试编写Django Rest Framework序列化程序。它应该接受简单的原始数据,并与一些相关的对象数据一起返回数据。我要序列化的模型是Model_C

以下是模型:

class ModelA(models.Model):
    name = models.Charfield(max_length=16)
    attr1 = models...
    ...

class ModelB(models.Model):
    name = models.CharField(max_length=16)
    attr1 = models...
    ...

class ModelC(models.Model):
    model_a = models.ForeignKey(ModelA)
    model_b = models.ForeignKey(ModelB)
    text = models.CharField(max_length=32)
    date = models.DateTimeField()

这是api视图:

class ModelCApiView(RetrieveUpdateDestroyAPIView):
    serializer_class = ModelCSerializer

    def get_queryset(self):
        return self.serializer_class.Meta.model.objects.all()

    def post(self, request):
        serializer = self.get_serializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(data=serializer.data, status=status.HTTP_201_CREATED)
        else:
            return Response(data=serializer.errors, status=status.HTTP_400_BAD_REQUEST)

馈送到序列化器的数据如下(带有或不带有ID):

{'id': '1', 'model_a': 1, 'model_b': 1, 'text': 'some text', 'date': '2019-08-28 08:00:00'}

现在,问题是序列化程序应该保存实例并返回带有一些相关数据的序列化模型:

{'id': '1', 'model_a': {'id': 1, 'name': 'some model a name'}, 'model_b': {'id': 1, 'name': 'some model b name', 'attr1': 'some attr value'}, 'text': 'some text', 'date': '2019-08-28 08:00:00'}

因此,唯一创建的是ModelC实例。 api视图不应创建ModelA或ModelB实例。

主要问题是对对象进行序列化/反序列化。我已经尝试了几件事:

  1. 最简单的
class ModelCSerializer(serializers.ModelSerializer):
    class Meta:
        model = ModelC
        fields = "__all__"

它会正确地反序列化请求,保存实例,但是返回的信息太少-仅相关对象的ID。


  1. 我用过深度
class ModelCSerializer(serializers.ModelSerializer):
    class Meta:
        model = ModelC
        fields = "__all__"
        depth = 1

这不起作用,因为它吐出500并显示错误:“列'model_a_id'不能为空”。


  1. 我尝试添加到2.声明的序列化器:
class ModelCSerializer(serializers.ModelSerializer):
    class Meta:
        model = ModelC
        fields = "__all__"
        depth = 1
    model_a = ModelASerializer()
    model_b = ModelBSerializer()

这会产生错误400,是错误的请求,因为它要求我将model_a和model_b的所有数据作为字典插入。


  1. 我在APIView中添加了行,以使用简单的序列化器序列化数据,然后使用更高级的序列化器再次序列化并返回更多信息。
    serializer = ModelCSerializer

    def post(self, request):
        serializer = self.get_serializer(data=request.data)
        if serializer.is_valid():
            obj = serializer.save()
            serializer = ModelCReadOnlySerializer(instance=obj, data=serializer.data)
            serializer.is_valid()
            return Response(data=serializer.data, status=status.HTTP_201_CREATED)
        else:
            return Response(data=serializer.errors, status=status.HTTP_400_BAD_REQUEST)


class ModelCReadOnlySerializer(serializers.ModelSerializer):
    class Meta:
        model = ModelC
        fields = ("id", "model_a", "model_b", "text", "date")
        read_only_fields = fields
        depth = 1

这是我迄今为止最接近的解决方案,因为它几乎返回了我需要的东西,除了它返回了model_a和model_b的所有数据,这太多了。


在这一点上,我还有其他想法,但我敢打赌,它们都很糟糕。我很高兴如何进行任何提示,因为我很近-> || <-在不使用DRF的情况下自行编写所有内容:-)

谢谢。

2 个答案:

答案 0 :(得分:0)

在@dirkgroten的大力帮助下:

第四个是要走的路。首先,必须有两个序列化器:一个用于写(按1.),另一个用于读:

class ModelCReadOnlySerializer(serializers.ModelSerializer):
    class Meta:
        model = ModelC
        fields = ("id", "model_a", "model_b", "text", "date")
        read_only_fields = fields
    # Not there is no depth any more
    model_a = ModelASerializer()
    model_b = ModelBSerializer()

在视图中,不需要将数据传递到只读序列化器中:

if serializer.is_valid():
            obj = serializer.save()
            serializer = ModelCReadOnlySerializer(instance=obj)
            return Response(data=serializer.data, status=status.HTTP_201_CREATED)
        else:
            return Response(data=serializer.errors, status=status.HTTP_400_BAD_REQUEST)

现在,数据以简单的形式接收,处理和保存,然后返回更多详细信息。

再次非常感谢!

答案 1 :(得分:0)

另一种方法是在序列化程序上重写.to_representation(instance)方法以自定义返回输出的方式。它占用了更多的空间,可读性却有所下降,但更加简洁,并将所有序列化逻辑都放入一个序列化器中。