web-dev-qa-db-ja.com

Django残りのフレームワーク、同じModelViewSetで異なるシリアライザーを使用

2つの異なるシリアライザーを提供しながら、ModelViewSetのすべての機能を活用できるようにしたいと思います。

  • オブジェクトのリストを表示するとき、各オブジェクトにその詳細にリダイレクトするURLがあり、他のすべてのリレーションがターゲットモデルの__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.
    }
155
BlackBear

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

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

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

異なるシリアライザーを提供することに関して、なぜ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

7
Luca Bezerra