简体   繁体   中英

How Can I Use Two Different Model Serializers With the Same Model?

I'm using django-rest-framework. I have a model with a relation. I would like to just display the count of related items when a user hits the /modelname/ URL, but show the full related set when a user hits a specific model instance at /modelname/1/ .

I can almost get what I want.

I have two serializers, like so:

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')

If I use the short version, I get the count of revisions (it's a calculated field). If I use the long version, I get the full list of related items as "revisions".

Short:

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

Long:

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

What I want to do is be able to switch between them based on query parameters (url). I tried to set the serializer_class to the ShortDataSetSerializer when the ID was not present, but it overrode all cases, not just the non-ID case.

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

Is there a way I can make this work? I realize I may be approaching this in a totally ridiculous manner, but it seems like there should be an easy solution.

The data example I gave rather abbreviated compared to the real data I'm working with. The end goal is to show a subset of fields in list view, and every field in the GET for a specific ID. This is a read-only API, so I don't need to worry about POST/PUT/DELETE.

You could do it by overriding the get_serializer_class method:

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

I think one easy solution for this problem would be to use class based generic views instead of a viewset.

You can use a list create api view with serializer_class as ShortDataSetSerializer. So when you get the list of data it will have the count of revisions. Also if you want the post request to work on the same url you will then have to override the get_serializer_class method to set the serializer_class based on request type.

For the retrieve view you can use the serializer_class as DataSetSerializer. It will have a list of revisions instead of count.

Checkout generic views api guide on DRF docs website.

Also, you can override the list and retrieve methods on the viewset, but I would prefer to use class based views since DRF has a lot of additional functionalities attached to the request functions like get, put etc.(or list, detail) and it is better not to override them.

Thank you, Benjamin. That didn't do what quite I was looking for. Ultimately what I had to do was this (with the same serializers as above):

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)

And in the urls.py:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM