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,
}
En cuanto a proporcionar diferentes serializadores, ¿por qué nadie va por el enfoque que verifica el método HTTP? Es más claro IMO y no requiere controles adicionales.
def get_serializer_class(self):
if self.request.method == ''POST'':
return NewRackItemSerializer
return RackItemSerializer
Créditos / fuente: https://github.com/encode/django-rest-framework/issues/1563#issuecomment-42357718
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.