Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to have different results for 'list' (players/) and 'detail' (players/{id})?

Here's the situation. I got a list on my Django REST API: /playerslist/

It returns me a list of players just like this one:

http://pastebin.com/JYA39gHT

This is exactly what I want for the moment. But now, I need this:

Going for /playerslist/1/ gives me different infos for the Player Number 1. The list is here only for listing players with basic informations. But I need detailed view for players, containing info from other models and with different serialization, it must be a basic issue, but as I'm totally new to Django and Python in general, I must misunderstanding something.

Here is my Viewset:

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

How can I achieve this ? Must I use @detail_route to have something like playerslist/1/detail ? I've already tried but DRF's documentation only show a single example and it's not clear at all for me.

like image 336
Sami Boudoukha Avatar asked Feb 10 '16 17:02

Sami Boudoukha


2 Answers

You can override the methods retrieve (returning one instance) or list (returning list obviously) as shown in first example in 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)

Where PlayerDetailSerializer is another serializer with different fields (whatever you need) and there is no need to specify it in serializer_class.

like image 101
Matúš Bartko Avatar answered Nov 14 '22 21:11

Matúš Bartko


To get different results when you do a 'detail' view, you want to change the serializer when doing a 'retrieve' call. I've done this with a custom mixin for a ModelViewSet, which expects a special "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)

Your viewset is simply:

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

Here, PlayersDetailSerializer is another Serializer that has more fields that you want to return.

As an aside, if you want to support optional filtering by teams, I would strongly recommend using django-filter. That way you don't have to worry about validation etc. Once installed, it's simply a case of adding this to your viewset:

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

See the docs for more info.

like image 28
misshapen Avatar answered Nov 14 '22 23:11

misshapen