Je souhaite fournir deux sérialiseurs différents tout en pouvant bénéficier de toutes les fonctionnalités de ModelViewSet
:
- Lors de la visualisation d'une liste d'objets, je voudrais que chaque objet ait une URL qui redirige vers ses détails et que toutes les autres relations apparaissent en utilisant
__unicode __
le 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"
]
}
- Lors de la visualisation des détails d'un objet, je souhaite utiliser la valeur par défaut
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 fonctionner tout cela 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.
Fondamentalement, je détecte quand l'utilisateur demande une vue de liste ou une vue détaillée et change serializer_class
pour répondre à mes besoins. Je ne suis pas vraiment satisfait de ce code, cela ressemble à un sale hack et, surtout, que se passe-t-il si deux utilisateurs demandent une liste et un détail au même moment?
Existe-t-il un meilleur moyen d'y parvenir en utilisant ModelViewSets
ou dois-je utiliser de nouveau GenericAPIView
?
EDIT:
Voici comment le 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.
}
la source
Réponses:
Remplacez votre
get_serializer_class
méthode. Cette méthode est utilisée dans vos mixins de modèle pour récupérer la classe Serializer appropriée.Notez qu'il existe également une
get_serializer
méthode qui renvoie une instance du sérialiseur correctla source
if hasattr(self, 'action') and self.action == 'list'
pk
objet de demande, si l'action estretrieve
?Vous pouvez trouver ce mixin utile, il remplace la méthode get_serializer_class et vous permet de déclarer un dict qui mappe l'action et la classe de sérialiseur ou de repli sur le comportement habituel.
la source
Cette réponse est la même que la réponse acceptée mais je préfère faire de cette manière.
Vues génériques
la source
En ce qui concerne la fourniture de différents sérialiseurs, pourquoi personne n'utilise l'approche qui vérifie la méthode HTTP? Il est plus clair IMO et ne nécessite aucun contrôle supplémentaire.
Crédits / source: https://github.com/encode/django-rest-framework/issues/1563#issuecomment-42357718
la source
list
et desretrieve
actions, vous avez le problème que les deux utilisent laGET
méthode. C'est pourquoi django rest framework ViewSets utilise le concept d' actions , qui est similaire, mais légèrement différent des méthodes http correspondantes.Sur la base des réponses @gonz et @ user2734679, j'ai créé ce petit package python qui donne cette fonctionnalité sous la forme d'une classe enfant de ModelViewset. Voici comment cela fonctionne.
la source
Bien que prédéfinir plusieurs sérialiseurs d'une manière ou d'une autre semble être la manière la plus clairement documentée , il existe une approche alternative qui s'appuie sur un autre code documenté et qui permet de passer des arguments au sérialiseur lors de son instanciation. Je pense que cela aurait probablement tendance à être plus utile si vous deviez générer une logique basée sur divers facteurs, tels que les niveaux d'administrateur des utilisateurs, l'action appelée, peut-être même les attributs de l'instance.
La première pièce du puzzle est la documentation sur la modification dynamique d'un sérialiseur au point d'instanciation . Cette documentation n'explique pas comment appeler ce code à partir d'un ensemble de vues ou comment modifier l'état en lecture seule des champs après leur lancement - mais ce n'est pas très difficile.
La deuxième pièce - le méthode get_serializer est également documentée - (juste un peu plus loin dans la page de get_serializer_class sous `` autres méthodes ''), il devrait donc être sûr de s'appuyer sur (et la source est très simple, ce qui, espérons-le, signifie moins de chance de effets secondaires résultant d'une modification). Vérifiez la source sous GenericAPIView (le ModelViewSet - et toutes les autres classes d'ensemble de vues intégrées semble-t-il - hérite du GenericAPIView qui définit get_serializer.
En mettant les deux ensemble, vous pourriez faire quelque chose comme ceci:
Dans un fichier de sérialiseurs (pour moi base_serializers.py):
Ensuite, dans votre ensemble de vues, vous pouvez faire quelque chose comme ceci:
Et ça devrait être ça! L'utilisation de MyViewSet devrait maintenant instancier votre MyDynamicSerializer avec les arguments que vous souhaitez - et en supposant que votre sérialiseur hérite de votre DynamicFieldsModelSerializer, il devrait savoir exactement quoi faire.
Cela vaut peut-être la peine de mentionner que cela peut avoir un sens particulier si vous voulez adapter le sérialiseur d'une autre manière… par exemple pour faire des choses comme prendre une liste read_only_exceptions et l'utiliser pour ajouter des champs à une liste blanche plutôt qu'à une liste noire (ce que j'ai tendance à faire). Je trouve également utile de définir les champs sur un tuple vide si ce n'est pas passé, puis de supprimer simplement la vérification de Aucun ... et j'ai défini mes définitions de champs sur mes sérialiseurs héritiers sur « tous ». Cela signifie qu'aucun champ qui n'est pas passé lors de l'instanciation du sérialiseur ne survit par accident et je n'ai pas non plus à comparer l'invocation du sérialiseur avec la définition de la classe du sérialiseur héréditaire pour savoir ce qui a été inclus ... par exemple dans l' init héréditaire du DynamicFieldsModelSerializer:
NB Si je voulais juste deux ou trois classes mappées sur des actions distinctes et / ou que je ne voulais pas de comportement de sérialiseur spécialement dynamique, je pourrais bien utiliser l'une des approches mentionnées par d'autres ici, mais j'ai pensé que cela valait la peine d'être présenté comme une alternative , compte tenu notamment de ses autres utilisations.
la source