Ich möchte zwei verschiedene Serializer bereitstellen und dennoch von allen Möglichkeiten profitieren ModelViewSet
:
- Wenn ich eine Liste von Objekten ansehe, möchte ich, dass jedes Objekt eine URL hat, die zu seinen Details umleitet, und dass jede andere Beziehung unter Verwendung
__unicode __
des Zielmodells angezeigt wird.
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 verwenden
HyperlinkedModelSerializer
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, all diese Arbeit 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 erkenne ich, wann der Benutzer eine Listenansicht oder eine Detailansicht anfordert, und ändere serializer_class
sie entsprechend meinen Anforderungen. Ich bin jedoch nicht wirklich zufrieden mit diesem Code, er sieht aus wie ein schmutziger Hack und vor allem, was ist, wenn zwei Benutzer gleichzeitig eine Liste und ein Detail anfordern?
Gibt es einen besseren Weg, um dies mit zu erreichen, ModelViewSets
oder muss ich mit zurückgreifen GenericAPIView
?
BEARBEITEN:
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.
}
quelle
Antworten:
Überschreiben Sie Ihre
get_serializer_class
Methode. Diese Methode wird in Ihren Modellmixins verwendet, um die richtige Serializer-Klasse abzurufen.Beachten Sie, dass es auch eine
get_serializer
Methode gibt, die eine Instanz des richtigen Serializers zurückgibtquelle
if hasattr(self, 'action') and self.action == 'list'
pk
angeforderte Objekt, wenn die Aktion ausgeführt wirdretrieve
?Dieses Mixin ist möglicherweise hilfreich. Es überschreibt die Methode get_serializer_class und ermöglicht es Ihnen, ein Diktat zu deklarieren, das die Aktion und die Serializer-Klasse oder den Fallback dem üblichen Verhalten zuordnet.
quelle
Diese Antwort ist die gleiche wie die akzeptierte Antwort, aber ich bevorzuge es auf diese Weise.
Allgemeine Ansichten
quelle
Warum entscheidet sich niemand für die Bereitstellung verschiedener Serializer für den Ansatz, der die HTTP-Methode überprüft? Es ist klarer IMO und erfordert keine zusätzlichen Überprüfungen.
Credits / Quelle: https://github.com/encode/django-rest-framework/issues/1563#issuecomment-42357718
quelle
list
undretrieve
Aktionen zu verwenden, besteht das Problem, dass beideGET
Methoden verwenden. Aus diesem Grund verwendet das Django Rest Framework ViewSets das Konzept von Aktionen , das ähnlich ist, sich jedoch geringfügig von den entsprechenden http-Methoden unterscheidet.Basierend auf den Antworten von @gonz und @ user2734679 habe ich dieses kleine Python-Paket erstellt , das diese Funktionalität in Form einer untergeordneten Klasse von ModelViewset bereitstellt . So funktioniert es.
quelle
Obwohl die Vordefinition mehrerer Serializer auf die eine oder andere Weise am offensichtlichsten dokumentiert zu sein scheint , gibt es bei FWIW einen alternativen Ansatz, der auf anderem dokumentiertem Code basiert und die Übergabe von Argumenten an den Serializer ermöglicht, wenn dieser instanziiert wird. Ich denke, es wäre wahrscheinlich lohnender, wenn Sie Logik generieren müssten, die auf verschiedenen Faktoren basiert, wie z. B. Benutzeradministrationsebenen, der aufgerufenen Aktion oder möglicherweise sogar Attributen der Instanz.
Der erste Teil des Puzzles ist die Dokumentation zum dynamischen Ändern eines Serialisierers zum Zeitpunkt der Instanziierung . In dieser Dokumentation wird nicht erläutert, wie dieser Code aus einem Viewset aufgerufen oder der schreibgeschützte Status von Feldern nach deren Initiierung geändert wird. Dies ist jedoch nicht sehr schwierig.
Das zweite Stück - die get_serializer-Methode ist ebenfalls dokumentiert - (etwas weiter unten auf der Seite von get_serializer_class unter "andere Methoden") sollte daher sicher sein (und die Quelle ist sehr einfach, was hoffentlich eine geringere Wahrscheinlichkeit für unbeabsichtigte Ereignisse bedeutet Nebenwirkungen infolge von Veränderungen). Überprüfen Sie die Quelle unter GenericAPIView (das ModelViewSet - und alle anderen integrierten Viewset-Klassen, wie es scheint - erben von der GenericAPIView, die get_serializer definiert.
Wenn Sie die beiden zusammenfügen, können Sie so etwas tun:
In einer Serializer-Datei (für mich base_serializers.py):
Dann könnten Sie in Ihrem Viewset Folgendes tun:
Und das sollte es sein! Die Verwendung von MyViewSet sollte jetzt Ihren MyDynamicSerializer mit den gewünschten Argumenten instanziieren - und vorausgesetzt, Ihr Serializer erbt von Ihrem DynamicFieldsModelSerializer, sollte er genau wissen, was zu tun ist.
Vielleicht ist es erwähnenswert, dass es besonders sinnvoll sein kann, wenn Sie den Serializer auf andere Weise anpassen möchten… z. B. um beispielsweise eine read_only_exceptions-Liste aufzunehmen und sie eher zur Whitelist als zu Blacklist-Feldern zu verwenden (was ich normalerweise tue). Ich finde es auch nützlich, die Felder auf ein leeres Tupel zu setzen, wenn es nicht übergeben wird, und dann einfach die Prüfung für Keine zu entfernen ... und meine Felddefinitionen für meine ererbenden Serializer auf " Alle " zu setzen. Dies bedeutet, dass keine Felder, die beim Instanziieren des Serializers nicht übergeben werden, versehentlich überleben, und ich muss auch den Serializer-Aufruf nicht mit der ererbenden Serializer-Klassendefinition vergleichen, um zu wissen, was enthalten ist ... z Init des DynamicFieldsModelSerializer:
NB Wenn ich nur zwei oder drei Klassen haben wollte, die unterschiedlichen Aktionen zugeordnet sind, und / oder ich kein besonders dynamisches Serializer-Verhalten wollte, könnte ich einen der von anderen hier erwähnten Ansätze verwenden, aber ich fand, dass dies eine Alternative wert ist , insbesondere angesichts seiner anderen Verwendungen.
quelle