viewsets viewset route get_object_or_404 framework detail decorators django serialization django-rest-framework

route - Marco de descanso de Django, use diferentes serializadores en el mismo ModelViewSet



get_object_or_404 django rest framework (4)

Me gustaría proporcionar dos serializadores diferentes y aún así poder beneficiarme de todas las instalaciones de ModelViewSet :

  • Al ver una lista de objetos, me gustaría que cada objeto tenga una url que redirecciona a sus detalles y todas las demás relaciones aparecen usando __unicode __ del modelo objetivo;

ejemplo:

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

  • Al ver los detalles de un objeto, me gustaría usar el HyperlinkedModelSerializer predeterminado

ejemplo:

{ "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/" ] }

Logré hacer todo este trabajo como quisiera de la siguiente manera:

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.

Básicamente detecto cuando el usuario está solicitando una vista de lista o una vista detallada y cambio serializer_class para satisfacer mis necesidades. Aunque no estoy realmente satisfecho con este código, parece un truco sucio y, lo más importante, ¿qué ocurre si dos usuarios solicitan una lista y un detalle en el mismo momento?

¿Hay una mejor manera de lograr esto usando ModelViewSets o tengo que recurrir a GenericAPIView ?

EDITAR:
A continuación, le mostramos cómo hacerlo utilizando un ModelViewSet base personalizado:

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. }


En base a las respuestas de @gonz y @ user2734679, he creado github.com/Darwesh27/drf-custom-viewsets que proporciona esta funcionalidad en forma de una clase hija de ModelViewset. Así es como funciona.

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, }



Puede encontrar esta mezcla útil, anula el método get_serializer_class y le permite declarar un dict que mapea la clase de acción y serializador o recurrir al comportamiento habitual.

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


Reemplace su método get_serializer_class . Este método se usa en los mixins de tu modelo para recuperar la clase de serializador adecuada.

Tenga en cuenta que también hay un método get_serializer que devuelve una instancia del Serializador correcto.

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.