Chciałbym udostępnić dwa różne serializatory, a jednocześnie móc korzystać ze wszystkich udogodnień ModelViewSet
:
- Podczas przeglądania listy obiektów chciałbym, aby każdy obiekt miał adres URL, który przekierowuje do jego szczegółów, a każda inna relacja pojawia się przy użyciu
__unicode __
modelu docelowego;
przykład:
{
"url": "http://127.0.0.1:8000/database/gruppi/2/",
"nome": "universitari",
"descrizione": "unitn!",
"creatore": "emilio",
"accesso": "CHI",
"membri": [
"emilio",
"michele",
"luisa",
"ivan",
"saverio"
]
}
- Podczas przeglądania szczegółów obiektu chciałbym użyć wartości domyślnej
HyperlinkedModelSerializer
przykład:
{
"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/"
]
}
Udało mi się sprawić, aby wszystko to działało, jak chcę, w następujący sposób:
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.
Zasadniczo wykrywam, kiedy użytkownik żąda widoku listy lub widoku szczegółowego, i zmieniam serializer_class
go w zależności od moich potrzeb. Nie jestem jednak bardzo zadowolony z tego kodu, wygląda na brudny hack, a co najważniejsze, jeśli dwóch użytkowników zażąda listy i szczegółów w tym samym momencie?
Czy istnieje lepszy sposób na osiągnięcie tego, ModelViewSets
czy muszę się wycofać GenericAPIView
?
EDYCJA:
Oto jak to zrobić przy użyciu niestandardowej bazy 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.
}
django
serialization
django-rest-framework
Czarny niedźwiedź
źródło
źródło
Odpowiedzi:
Zastąp swoją
get_serializer_class
metodę. Ta metoda jest używana w mieszankach modelu w celu pobrania odpowiedniej klasy Serializer.Zauważ, że istnieje również
get_serializer
metoda, która zwraca instancję poprawnego serializatoraźródło
if hasattr(self, 'action') and self.action == 'list'
pk
żądany obiekt, jeśli akcja jest wykonywanaretrieve
?Może się okazać, że ten mixin jest użyteczny, zastępuje metodę get_serializer_class i pozwala zadeklarować dykt, który odwzorowuje akcję i klasę serializatora lub przywraca normalne zachowanie.
źródło
Ta odpowiedź jest taka sama jak odpowiedź zaakceptowana, ale wolę to zrobić w ten sposób.
Ogólne widoki
źródło
Jeśli chodzi o zapewnianie różnych serializatorów, dlaczego nikt nie wybiera podejścia sprawdzającego metodę HTTP? Jest to wyraźniejsze IMO i nie wymaga żadnych dodatkowych kontroli.
Kredyty / źródło: https://github.com/encode/django-rest-framework/issues/1563#issuecomment-42357718
źródło
list
iretrieve
akcji, masz problem z obiemaGET
metodami. Dlatego właśnie django rest framework ViewSets używa pojęcia akcji , która jest podobna, ale nieco inna niż odpowiednie metody http.Na podstawie odpowiedzi @gonz i @ user2734679 stworzyłem ten mały pakiet python, który daje tę funkcjonalność w formie klasy potomnej ModelViewset . Oto jak to działa.
źródło
Chociaż wstępne zdefiniowanie wielu Serializatorów w taki czy inny sposób wydaje się być najbardziej udokumentowanym sposobem, FWIW jest alternatywnym podejściem, które opiera się na innym udokumentowanym kodzie i umożliwia przekazywanie argumentów do serializatora podczas jego tworzenia. Myślę, że byłoby bardziej opłacalne, gdybyś musiał wygenerować logikę na podstawie różnych czynników, takich jak poziomy administratora użytkowników, wywoływane działanie, a może nawet atrybuty instancji.
Pierwszym elementem układanki jest dokumentacja dynamicznej modyfikacji serializatora w momencie tworzenia wystąpienia . Ta dokumentacja nie wyjaśnia, jak wywołać ten kod z zestawu widoków ani jak zmodyfikować status pola tylko do odczytu po ich zainicjowaniu - ale to nie jest bardzo trudne.
Drugi kawałek - metoda get_serializer jest również udokumentowany - (nieco dalej w dół strony od get_serializer_class w „innych metodach”), więc warto na nim polegać (a źródło jest bardzo proste, co, mam nadzieję, oznacza mniejszą szansę na niezamierzone skutki uboczne wynikające z modyfikacji). Sprawdź źródło w GenericAPIView (ModelViewSet - i wydaje się, że wszystkie inne wbudowane klasy widoków - dziedziczą po GenericAPIView, który definiuje get_serializer.
Łącząc te dwie rzeczy, możesz zrobić coś takiego:
W pliku serializerów (dla mnie base_serializers.py):
Następnie w swoim widoku możesz zrobić coś takiego:
I to powinno być to! Korzystanie z MyViewSet powinno teraz utworzyć instancję MyDynamicSerializer z argumentami, które chcesz - i zakładając, że twój serializator dziedziczy po DynamicFieldsModelSerializer, powinien wiedzieć, co robić.
Być może warto wspomnieć, że może to mieć szczególny sens, jeśli chcesz dostosować serializator na inne sposoby ... np. Zrobić rzeczy takie jak pobranie listy read_only_exceptions i użycie jej do białej listy zamiast pól czarnej listy (co zwykle robię). Przydaje mi się również ustawienie pól na pustą krotkę, jeśli nie została ona przekazana, a następnie usunięcie zaznaczenia opcji Brak ... i ustawiłem definicje pól w dziedziczących serializatorach na „ wszystkie ”. Oznacza to, że żadne pola, które nie zostaną przekazane podczas tworzenia instancji serializatora, przetrwają przypadkowo, a ja nie muszę porównywać wywołania serializatora z dziedziczącą definicją klasy serializatora, aby wiedzieć, co zostało uwzględnione ... np. W trakcie inicjowania DynamicFieldsModelSerializer:
NB: Gdybym chciał tylko dwie lub trzy klasy, które byłyby przypisane do różnych akcji i / lub nie chciałbym żadnego specjalnie dynamicznego zachowania serializatora, mógłbym użyć jednego z podejść wymienionych tutaj przez innych, ale pomyślałem, że warto to przedstawić jako alternatywę , szczególnie biorąc pod uwagę jego inne zastosowania.
źródło