wake-up-neo.com

Django Rest Framework, verwenden Sie verschiedene Serializer im selben ModelViewSet

Ich möchte zwei verschiedene Serializer bereitstellen und trotzdem von allen Funktionen von ModelViewSet profitieren:

  • Wenn ich eine Liste von Objekten betrachte, möchte ich, dass jedes Objekt eine URL hat, die zu seinen Details umleitet, und jede andere Beziehung erscheint mit __unicode __ des Zielmodells;

beispiel:

{
  "url": "http://127.0.0.1:8000/database/gruppi/2/",
  "nome": "universitari",
  "descrizione": "unitn!",
  "creatore": "emilio",
  "accesso": "CHI",
  "membri": [
    "emilio",
    "michele",
    "luisa",
    "ivan",
    "saverio"
  ]
}
  • Beim Anzeigen der Details eines Objekts möchte ich die Standardeinstellung HyperlinkedModelSerializer verwenden.

beispiel:

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

Ich habe es geschafft, alles so zu machen, wie ich es mir wünsche:

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.

Grundsätzlich stelle ich fest, wann der Benutzer eine Listenansicht oder eine Detailansicht anfordert und ändere serializer_class nach meinen Wünschen. Ich bin mit diesem Code nicht wirklich zufrieden, es sieht aus wie ein schmutziger Hack und vor allem , wenn zwei Benutzer gleichzeitig eine Liste und ein Detail anfordern?

Gibt es einen besseren Weg, dies mit ModelViewSets zu erreichen, oder muss ich mit GenericAPIView zurückgreifen?

EDIT:
So geht's mit einer benutzerdefinierten Basis 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

Überschreibe dein get_serializer_class Methode. Diese Methode wird in Ihren Modellmixins verwendet, um die richtige Serializer-Klasse abzurufen.

Beachten Sie, dass es auch ein get_serializer Methode, die ein Instanz des richtigen Serializers zurückgibt

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

Sie finden dieses Mixin möglicherweise nützlich, es überschreibt die get_serializer_class-Methode und ermöglicht es Ihnen, ein Diktat zu deklarieren, das die Aktion und die Serializer-Klasse oder den Fallback auf das übliche Verhalten abbildet.

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

Basierend auf den Antworten von @gonz und @ user2734679 habe ich dieses kleine python package erstellt, das diese Funktionalität in Form einer untergeordneten Klasse von ModelViewset bereitstellt.

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

Warum wählt niemand den Ansatz, der die HTTP-Methode überprüft, wenn es darum geht, verschiedene Serialisierer bereitzustellen? Es ist klarer IMO und erfordert keine zusätzlichen Prüfungen.

def get_serializer_class(self):
    if self.request.method == 'POST':
        return NewRackItemSerializer
    return RackItemSerializer

Credits/Quelle: https://github.com/encode/Django-rest-framework/issues/1563#issuecomment-42357718

7
Luca Bezerra