Je voudrais fournir deux sérialiseurs différents et pouvoir néanmoins bénéficier de toutes les installations de ModelViewSet
:
__unicode __
du modèle cible;exemple:
{
"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
exemple:
{
"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/"
]
}
J'ai réussi à faire tout ce travail comme je le souhaite de la manière suivante:
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.
En gros, je détecte le moment où l'utilisateur demande une vue en liste ou une vue détaillée et je modifie serializer_class
pour l'adapter à mes besoins. Cependant, je ne suis pas vraiment satisfait de ce code, il ressemble à un bidouillage sale et, plus important encore, si deux utilisateurs demandent une liste et un détail au même moment?
Existe-t-il un meilleur moyen d'utiliser ModelViewSets
ou dois-je me replier sur GenericAPIView
?
EDIT:
Voici comment faire en utilisant une base personnalisée 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.
}
Remplacez votre méthode get_serializer_class
. Cette méthode est utilisée dans vos mixins de modèles pour récupérer la classe de sérialiseur appropriée.
Notez qu'il existe également une méthode get_serializer
qui retourne une instance du sérialiseur correct
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.
Vous pouvez trouver ce mélange utile, il écrasera la méthode get_serializer_class et vous permettra de déclarer un dict qui mappera une classe d’action et de sérialiseur ou adoptera le comportement habituel.
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()
Sur la base de @gonz et de @ user2734679, j'ai créé ce petit paquetage python qui donne à cette fonctionnalité la forme d'une classe enfant de ModelViewset. Voici comment cela fonctionne.
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 ce qui concerne la fourniture de différents sérialiseurs, pourquoi personne ne se lance dans une approche qui vérifie la méthode HTTP? C'est plus clair IMO et ne nécessite aucune vérification supplémentaire.
def get_serializer_class(self):
if self.request.method == 'POST':
return NewRackItemSerializer
return RackItemSerializer
Crédits/source: https://github.com/encode/Django-rest-framework/issues/1563#issuecomment-42357718