Django rest framework, использовать различные сериализаторы в том же ModelViewSet



я хотел бы предоставить два разных сериализатора и все же иметь возможность воспользоваться всеми возможностями ModelViewSet:




  • при просмотре списка объектов, я хотел бы, чтобы каждый объект имел url-адрес, который перенаправляет на его детали и все другие отношения появляются с помощью __unicode __ целевой модели;


пример:



{
"url": "http://127.0.0.1:8000/database/gruppi/2/",
"nome": "universitari",
"descrizione": "unitn!",
"creatore": "emilio",
"accesso": "CHI",
"membri": [
"emilio",
"michele",
"luisa",
"ivan",
"saverio"
]
}



  • при просмотре деталей объекта, я хотел бы использовать значение по умолчанию HyperlinkedModelSerializer


пример:



{
"url": "http://127.0.0.1:8000/database/gruppi/2/",
"nome": "universitari",
"descrizione": "unitn!",
"creatore": "http://127.0.0.1:8000/database/utenti/3/",
"accesso": "CHI",
"membri": [
"http://127.0.0.1:8000/database/utenti/3/",
"http://127.0.0.1:8000/database/utenti/4/",
"http://127.0.0.1:8000/database/utenti/5/",
"http://127.0.0.1:8000/database/utenti/6/",
"http://127.0.0.1:8000/database/utenti/7/"
]
}


мне удалось сделать всю эту работу, как я хочу, следующим образом:



serializers.py



# serializer to use when showing a list
class ListaGruppi(serializers.HyperlinkedModelSerializer):
membri = serializers.RelatedField(many = True)
creatore = serializers.RelatedField(many = False)

class Meta:
model = models.Gruppi

# serializer to use when showing the details
class DettaglioGruppi(serializers.HyperlinkedModelSerializer):
class Meta:
model = models.Gruppi


views.py



class DualSerializerViewSet(viewsets.ModelViewSet):
"""
ViewSet providing different serializers for list and detail views.

Use list_serializer and detail_serializer to provide them
"""
def list(self, *args, **kwargs):
self.serializer_class = self.list_serializer
return viewsets.ModelViewSet.list(self, *args, **kwargs)

def retrieve(self, *args, **kwargs):
self.serializer_class = self.detail_serializer
return viewsets.ModelViewSet.retrieve(self, *args, **kwargs)

class GruppiViewSet(DualSerializerViewSet):
model = models.Gruppi
list_serializer = serializers.ListaGruppi
detail_serializer = serializers.DettaglioGruppi

# etc.


в основном я обнаруживаю, когда пользователь запрашивает представление списка или подробное представление и изменение serializer_class чтобы удовлетворить мои потребности. Я не очень доволен этим кодом, хотя, это выглядит как грязный хак и, самое главное, что делать, если два пользователя запрашивают список и детали одновременно?



есть ли лучший способ добиться этого, используя ModelViewSets или я должен отступить с помощью GenericAPIView?



EDIT:

Вот как это сделать с помощью пользовательской базы ModelViewSet:



class MultiSerializerViewSet(viewsets.ModelViewSet):
serializers = {
'default': None,
}

def get_serializer_class(self):
return self.serializers.get(self.action,
self.serializers['default'])

class GruppiViewSet(MultiSerializerViewSet):
model = models.Gruppi

serializers = {
'list': serializers.ListaGruppi,
'detail': serializers.DettaglioGruppi,
# etc.
}
744   4  

4 ответов:

отменить get_serializer_class метод. Этот метод используется в модели mixins для получения соответствующего класса сериализатора.

обратите внимание, что есть также get_serializer метод, который возвращает экземпляр правильный сериализатор

class DualSerializerViewSet(viewsets.ModelViewSet):
    def get_serializer_class(self):
        if self.action == 'list':
            return serializers.ListaGruppi
        if self.action == 'retrieve':
            return serializers.DettaglioGruppi
        return serializers.Default # I dont' know what you want for create/destroy/update.                

вы можете найти этот mixin полезным, он переопределяет метод get_serializer_class и позволяет объявить dict, который отображает действие и класс сериализатора или резервный вариант обычного поведения.

class MultiSerializerViewSetMixin(object):
    def get_serializer_class(self):
        """
        Look for serializer class in self.serializer_action_classes, which
        should be a dict mapping action name (key) to serializer class (value),
        i.e.:

        class MyViewSet(MultiSerializerViewSetMixin, ViewSet):
            serializer_class = MyDefaultSerializer
            serializer_action_classes = {
               'list': MyListSerializer,
               'my_action': MyActionSerializer,
            }

            @action
            def my_action:
                ...

        If there's no entry for that action then just fallback to the regular
        get_serializer_class lookup: self.serializer_class, DefaultSerializer.

        """
        try:
            return self.serializer_action_classes[self.action]
        except (KeyError, AttributeError):
            return super(MultiSerializerViewSetMixin, self).get_serializer_class()

на основе ответов @gonz и @user2734679 я создал этот небольшой пакет python что дает эту функциональность в виде дочернего класса ModelViewset. Вот как это работает.

from drf_custom_viewsets.viewsets.CustomSerializerViewSet
from myapp.serializers import DefaltSerializer, CustomSerializer1, CustomSerializer2

class MyViewSet(CustomSerializerViewSet):
    serializer_class = DefaultSerializer
    custom_serializer_classes = {
        'create':  CustomSerializer1,
        'update': CustomSerializer2,
    }

Что касается предоставления различных сериализаторов, почему никто не собирается использовать подход, который проверяет метод HTTP? Это понятнее ИМО и не требует дополнительных проверок.

def get_serializer_class(self):
    if self.request.method == 'POST':
        return NewRackItemSerializer
    return RackItemSerializer

кредиты/источник: https://github.com/encode/django-rest-framework/issues/1563#issuecomment-42357718

Comments

    Ничего не найдено.