【问题标题】:How to have different results for 'list' (players/) and 'detail' (players/{id})?'list' (players/) 和 'detail' (players/{id}) 如何得到不同的结果?
【发布时间】:2016-05-21 05:26:53
【问题描述】:

情况是这样的。我的 Django REST API 上有一个列表:/playerslist/

它返回给我一个像这样的玩家列表:

http://pastebin.com/JYA39gHT

这正是我目前想要的。但现在,我需要这个:

前往/playerslist/1/ 为我提供了 1 号球员的不同信息。此列表仅用于列出球员的基本信息。但是我需要玩家的详细视图,包含来自其他模型的信息并且具有不同的序列化,这一定是一个基本问题,但是由于我对 Django 和 Python 完全陌生,所以我一定会误解一些东西。

这是我的视图集:

class PlayersListViewSet(viewsets.ModelViewSet):
    queryset = Player.objects.all()
    serializer_class = PlayersListSerializer
    http_method_names = ['get', 'post']
    pagination_class = None
    filter_backends = [filters.OrderingFilter]
    ordering_fields = ['name']

   def get_queryset(self):
      queryset = Player.objects.all()
      team_id = self.request.query_params.get('team', None)

      if team_id:
          try:
              queryset = queryset.filter(team=team_id)
          except ValueError:
              raise exceptions.ParseError()
       return queryset

我怎样才能做到这一点?我必须使用 @detail_route 来拥有类似 playerslist/1/detail 的东西吗?我已经尝试过了,但 DRF 的文档只显示了一个示例,对我来说根本不清楚。

【问题讨论】:

  • 你检查过这个吗? django-rest-framework.org/api-guide/viewsets 在 UserViewSet 类中有显示检索方法(对于 1 个实例)和列表方法的示例。您可以为每个方法声明不同的序列化程序。
  • 这就像一个魅力,我一开始尝试过,但我一定做错了我累了,无论如何感谢您的评论,将其作为答案发布,以便我接受它。
  • 我将其发布为答案。我很高兴它对你有用!

标签: python django django-rest-framework django-queryset django-serializer


【解决方案1】:

要在执行“详细”视图时获得不同的结果,您需要在执行“检索”调用时更改序列化程序。我已经为 ModelViewSet 使用了一个自定义 mixin,它需要一个特殊的“detail_serializer_class”:

class DifferentDetailSerializerMixin(object):
  """
  For a viewset, mix this in to use a different serializer class
  for individual 'retrieve' views, different from the standard 
  serializer for lists.
  """
  def retrieve(self, request, *args, **kwargs):
      instance = self.get_object()
      serializer = self.detail_serializer_class(instance, context=self.get_serializer_context())
      return Response(serializer.data)

您的视图集很简单:

class PlayersListViewSet(DifferentDetailSerializerMixin, viewsets.ModelViewSet):
    queryset = Player.objects.all()
    serializer_class = PlayersListSerializer
    detail_serializer_class = PlayersDetailSerializer
    filter_backends = [filters.OrderingFilter]
    ordering_fields = ['name']

这里,PlayersDetailSerializer 是另一个序列化器,它有更多要返回的字段。

顺便说一句,如果您想支持按团队进行可选过滤,我强烈建议您使用 django-filter。这样您就不必担心验证等问题。安装后,只需将其添加到您的视图集中即可:

filter_backends = (filters.OrderingFilter, filters.DjangoFilterBackend, )
filter_fields = ['team']

请参阅the docs 了解更多信息。

【讨论】:

  • 我必须使用自定义 Mixin 吗?我不能简单地覆盖我的 ViewSet 中的检索方法吗?您的解决方案看起来不错,但看起来它不允许我使用另一个查询集,对吗? (我不知道我是否需要这样做,但我需要知道这种方式可以让我做什么。)谢谢
  • 当然,如果你愿意,你可以直接重写retrieve方法;我刚刚发现这种模式更整洁,因为我发现自己经常需要这种模式。只需将“检索”方法向下移动并将其直接放入您的 ViewSet 中。
  • 这就像 Matúš Bartko 的方式:覆盖检索方法完成这项工作。
  • 嗯,好吗?我为您提供了可以回答您的问题的工作代码(retrieve 方法上的自定义序列化程序),一个很好的 mixin 示例以澄清您的代码,一个改进您的过滤的建议,以及进一步阅读的链接。我不知道你还能如何回答你的问题。
  • 您的回答很好并且有效,但我发现 Markus 的回答对我来说更容易理解。
【解决方案2】:

您可以覆盖方法检索(返回一个实例)或列表(显然返回列表),如http://www.django-rest-framework.org/api-guide/viewsets/ 中的第一个示例所示。

class PlayersListViewSet(viewsets.ModelViewSet):
    queryset = Player.objects.all()
    serializer_class = PlayersListSerializer
    http_method_names = ['get', 'post']
    pagination_class = None
    filter_backends = [filters.OrderingFilter]
    ordering_fields = ['name']

   def get_queryset(self):
      queryset = Player.objects.all()
      team_id = self.request.query_params.get('team', None)

      if team_id:
          try:
              queryset = queryset.filter(team=team_id)
          except ValueError:
              raise exceptions.ParseError()
       return queryset

   def retrieve(self, request, *args, **kwargs):
       instance = self.get_object()
       serializer = PlayerDetailSerializer(instance)
       return Response(serializer.data)

其中 PlayerDetailSerializer 是另一个具有不同字段的序列化器(无论您需要什么),无需在 serializer_class 中指定。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-12-12
    • 1970-01-01
    • 2023-03-19
    • 1970-01-01
    • 2022-01-10
    • 2023-03-26
    • 1970-01-01
    • 2019-09-26
    相关资源
    最近更新 更多