首页 文章

Django休息框架,在同一个ModelViewSet中使用不同的序列化器

提问于
浏览
123

我想提供两种不同的序列化器,但能够从 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 以满足我的需要 . 我对这段代码并不是很满意,它看起来像是一个肮脏的黑客,最重要的是,如果两个用户在同一时刻请求列表和细节怎么办?

有没有更好的方法来实现这个 ModelViewSets 或者我是否必须使用 GenericAPIView

EDIT:
以下是使用自定义库 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.
    }

4 回答

  • 4

    基于@gonz和@ user2734679的答案,我创建了this small python package,它在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,
        }
    
  • 6

    覆盖 get_serializer_class 方法 . 在模型mixins中使用此方法来检索正确的Serializer类 .

    请注意,还有一个 get_serializer 方法,它返回正确的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.
    
  • 188

    您可能会发现这个mixin很有用,它会覆盖get_serializer_class方法,并允许您声明一个将动作和序列化程序类或回退映射到通常行为的dict .

    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()
    
  • 61

    关于提供不同的序列化程序,为什么没有人选择检查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

相关问题