2つの異なるシリアライザーを提供しながら、ModelViewSet
のすべての機能を活用できるようにしたいと思います。
__unicode __
を使用して表示されるようにします。例:
{
"url": "http://127.0.0.1:8000/database/gruppi/2/",
"nome": "universitari",
"descrizione": "unitn!",
"creatore": "emilio",
"accesso": "CHI",
"membri": [
"emilio",
"michele",
"luisa",
"ivan",
"saverio"
]
}
HyperlinkedModelSerializer
を使用したい例:
{
"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/"
]
}
私は次のようにして、このすべてをうまくやり遂げることができました。
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.
基本的に、ユーザーがリストビューまたは詳細ビューを要求していることを検出し、serializer_class
をニーズに合わせて変更します。私はこのコードに本当に満足していません、それは汚いハックのように見えます、そして最も重要なことには、2人のユーザーが同時にリストと詳細を要求したらどうなりますか?
ModelViewSets
を使用してこれを達成するより良い方法はありますか、またはGenericAPIView
を使用してフォールバックする必要がありますか?
編集:
カスタムベース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.
}
get_serializer_class
メソッドをオーバーライドします。このメソッドは、適切なSerializerクラスを取得するためにモデルミックスインで使用されます。
正しいシリアライザーのinstanceを返すget_serializer
メソッドもあることに注意してください
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.
このmixinは便利です。get_serializer_classメソッドをオーバーライドし、アクションとシリアライザークラスまたはフォールバックを通常の動作にマッピングする辞書を宣言できます。
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()
@gonzと@ user2734679の回答に基づいて、私が作成した この小さなpythonパッケージ は、ModelViewsetの子クラスの形式でこの機能を提供します。仕組みは次のとおりです。
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,
}
異なるシリアライザーを提供することに関して、なぜHTTPメソッドをチェックするアプローチに誰も行かないのですか? IMOはより明確で、追加のチェックは不要です。
def get_serializer_class(self):
if self.request.method == 'POST':
return NewRackItemSerializer
return RackItemSerializer
クレジット/ソース: https://github.com/encode/Django-rest-framework/issues/1563#issuecomment-42357718