web-dev-qa-db-fra.com

Django Rest framework, utilisez différents sérialiseurs dans le même ModelViewSet

Je voudrais fournir deux sérialiseurs différents et pouvoir néanmoins bénéficier de toutes les installations de ModelViewSet:

  • Lors de l'affichage d'une liste d'objets, j'aimerais que chaque objet ait une URL qui redirige vers ses détails et que toute autre relation apparaisse à l'aide de __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"
  ]
}
  • Lors de l'affichage des détails d'un objet, j'aimerais 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 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.
    }
155
BlackBear

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.                
238
user133688

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

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

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

7
Luca Bezerra