【问题标题】:How Can I Use Two Different Model Serializers With the Same Model?如何在同一个模型中使用两个不同的模型序列化器?
【发布时间】:2014-03-23 15:19:27
【问题描述】:

我正在使用 django-rest-framework。我有一个有关系的模型。我只想在用户点击/modelname/ URL 时显示相关项目的数量,但在用户点击/modelname/1/ 的特定模型实例时显示完整的相关集。

我可以几乎得到我想要的。

我有两个序列化器,如下所示:

class DataSetSerializer(serializers.ModelSerializer):
    revisions = serializers.RelatedField(source='datasetrevision_set', many=True)

    class Meta:
        model = DataSet
        fields = ('id', 'title', 'revisions')

class ShortDataSetSerializer(serializers.ModelSerializer):

    class Meta:
        model = DataSet
        fields = ('id', 'title', 'revisions')

如果我使用简短版本,我会得到修订计数(这是一个计算字段)。如果我使用长版本,我会获得相关项目的完整列表作为“修订”。

短:

[{"id": 1, "title": "My Data Set", "revisions": 0}]

长:

[{"id": 1, "title": "My Data Set", "revisions": ["Data Set v1", "Data Set v2"]}]

我想要做的是能够根据查询参数(url)在它们之间切换。当 ID 不存在时,我尝试将 serializer_class 设置为 ShortDataSetSerializer,但它会覆盖所有情况,而不仅仅是非 ID 情况。

class DataSetViewSet(viewsets.ModelViewSet):
    serializer_class = DataSetSerializer
    model = DataSet

    def get_queryset(self):
       try:
           id = self.kwargs['id']
           queryset = DataSet.objects.filter(id=id)
       except KeyError:
           queryset = DataSet.objects.all()
           # We want to only list all of the revision data if we're viewing a
           # specific set, but this overrides for all cases, not just the one
           # we want.
           self.serializer_class = ShortDataSetSerializer
       return queryset

有什么方法可以让我完成这项工作吗?我意识到我可能会以一种完全荒谬的方式来解决这个问题,但似乎应该有一个简单的解决方案。

与我正在使用的真实数据相比,我给出的数据示例相当简略。最终目标是在列表视图中显示字段的子集,并在 GET 中显示特定 ID 的每个字段。这是一个只读 API,所以我不需要担心 POST/PUT/DELETE。

【问题讨论】:

  • 简单但更多余的答案是只使用两个视图集。
  • 您是否尝试过覆盖get_serializer_class 方法?

标签: python json django rest django-rest-framework


【解决方案1】:

您可以通过重写 get_serializer_class 方法来做到这一点:

class DataSetViewSet(viewsets.ModelViewSet):
    model = DataSet

    def get_queryset(self):
       queryset = DataSet.objects.all()
       if self.kwargs.get('id'):
          queryset = queryset.filter(pk=self.kwargs.get('id'))
       return queryset

    def get_serializer_class(self):
       return DataSetSerializer if 'id' in self.kwargs else ShortDataSetSerializer

【讨论】:

    【解决方案2】:

    我认为解决这个问题的一个简单方法是使用基于类的通用视图而不是视图集。

    您可以使用带有 serializer_class 的列表创建 api 视图作为 ShortDataSetSerializer。因此,当您获得数据列表时,它将具有修订数。此外,如果您希望发布请求在相同的 url 上工作,则必须覆盖 get_serializer_class 方法以根据请求类型设置 serializer_class。

    对于检索视图,您可以使用 serializer_class 作为 DataSetSerializer。它将有一个修订列表而不是计数。

    查看 DRF 文档网站上的通用视图 API 指南。

    此外,您可以覆盖视图集上的列表和检索方法,但我更喜欢使用基于类的视图,因为 DRF 具有许多附加到请求函数的附加功能,例如 get、put 等(或 list、detail ),最好不要覆盖它们。

    【讨论】:

      【解决方案3】:

      谢谢你,本杰明。那并没有完全满足我的要求。最终我要做的是(使用与上面相同的序列化程序):

      class DataSetViewSet(viewsets.ModelViewSet):
          model = DataSet
      
          def list(self, request):
              queryset = DataSet.objects.all()
              serializer = ShortDataSetSerializer(queryset, many=True)
              return Response(serializer.data)
      
          def detail(self, request, id=None):
              queryset = DataSet.objects.get(id=id)
              serializer = DataSetSerializer(queryset)
              return Response(serializer.data)
      

      在 urls.py 中:

      url(r'^sets/$', views.DataSetViewSet.as_view({'get': 'list'})),
      url(r'^sets/(?P<id>\d+)/$', views.DataSetViewSet.as_view({'get': 'detail'})),
      

      【讨论】:

      • ModelViewSet 是一个 GenericViewSet。这里最好的解决方案实际上是覆盖get_serializer_classget_serializer,因为通过覆盖listdetail 您复制了ModelViewSet 中已经存在的功能(并且您删除了过滤器和权限后端等功能) .对于 urls.py 中的代码,您可以使用 DRF 路由器(SimpleRouter、DefaultRouter)
      猜你喜欢
      • 2014-06-06
      • 1970-01-01
      • 1970-01-01
      • 2022-11-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-02-04
      • 1970-01-01
      相关资源
      最近更新 更多