【问题标题】:Problem with writing a django rest serializer编写 django rest 序列化程序的问题
【发布时间】:2020-01-03 18:21:28
【问题描述】:

我正在尝试编写一个 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 错误:“Column 'model_a_id' cannot be null”。


  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 的所有数据,这太多了。


此时我还有其他想法,但我敢打赌它们都很糟糕。我很高兴有任何提示如何继续,因为我已经很接近了-> ||

提前谢谢你。

【问题讨论】:

  • 对我来说,第一个模糊的事情是你在 RetrieveUpdate Destroy View 中使用'post'方法。此类用于删除、更新和检索(delete、put、patch、ger)而不是用于创建对象,并且您正在对其使用 post 方法,这是不允许的。您应该一直在使用 ListAPIView 或 ListCreateAPIView
  • 对于您的只读序列化程序,您仍应使用 ModelASerializer 和 ModelBSerializer 来指定要返回的字段。我认为这是您需要对方法 4 进行的小调整。
  • @dirkgroten 我刚才试过了。序列化程序现在只返回指定为序列化程序的字段,完全忽略 'fields' Meta: {"model_a": 1, "model_b"}
  • 你不应该向它传递数据,因为你只是在序列化一个现有的对象。

标签: python django serialization django-rest-framework


【解决方案1】:

在@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)

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

再次感谢!

【讨论】:

    【解决方案2】:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-10-22
      • 1970-01-01
      • 1970-01-01
      • 2017-03-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-07-30
      相关资源
      最近更新 更多