【问题标题】:Django Rest Framework, HyperlinkedModelSerializers, ModelViewSets, and writable GenericForeignKeys: how?Django Rest Framework、HyperlinkedModelSerializers、ModelViewSets 和可写的 GenericForeignKeys:怎么样?
【发布时间】:2014-11-11 06:50:14
【问题描述】:

我有一个模型 FinancialTransaction,它具有典型的 content_typeobject_idcontent_object 字段来设置与我的任何其他模型的通用关系。

我已经想出了如何序列化这个关系以供阅读:

class FinancialTransactionSerializer(serializers.HyperlinkedModelSerializer):
    content_object = serializers.SerializerMethodField('get_content_obj_url')

    def get_content_obj_url(self, obj):
        obj = obj.content_object

        view_name = obj._meta.object_name.lower() + "-detail"
        s = serializers.HyperlinkedIdentityField(source=obj, view_name=view_name)
        s.initialize(self, None)
        return s.field_to_native(obj, None)

    class Meta:
        model = FinancialTransaction
        fields = ('id', 'value', 'date', 'memo', 'banking_account', 'content_object')

视图集:

class FinancialTransactionViewSet(viewsets.ModelViewSet):
    model = FinancialTransaction
    serializer_class = FinancialTransactionSerializer

当我对视图执行 GET 操作时,这将为序列化表示的相关对象创建一个超链接。

但是,我有点纠结于如何制作它,以便我可以使用已经存在的相关对象发布一个新的 FinancialTransaction。

理想情况下,它可以像普通的 ForeignKey 一样工作,我可以在其中发布类似的内容:

{"value": "200.00",
 "date": "2014-10-10",
 "memo": "repairs",
 "banking_account": "http://domain.com/api/banking_account/134/",
 "content_object": "http://domain.com/api/property/432/"
}

【问题讨论】:

    标签: django django-rest-framework


    【解决方案1】:

    好吧,回答我自己的问题...

    我在自己的序列化程序中覆盖了restore_fields,如下所示:

    class FinancialTransactionSerializer(serializers.HyperlinkedModelSerializer):
        content_object = serializers.SerializerMethodField('get_content_obj_url')
    
        def get_content_obj_url(self, obj):
            obj = obj.content_object
    
            view_name = get_view_name(obj)
            s = serializers.HyperlinkedIdentityField(source=obj, view_name=view_name)
            s.initialize(self, None)
            return s.field_to_native(obj, None)
    
        def restore_fields(self, data, files):
            content_object = None
    
            if 'content_object' in data:
                request = self.context.get('request')  
                content_object = get_object_from_url(request.DATA['content_object'])
    
            attrs = super(FinancialTransactionSerializer, self).restore_fields(data, files)
            if content_object:
                attrs['content_object'] = content_object
            return attrs
    
        class Meta:
            model = FinancialTransaction
            fields = ('id', 'value', 'date', 'memo', 'banking_account', 'content_object')
    
    def get_model_from_url(url: str):
        return resolve(urlparse(url).path).func.cls.model
    
    def get_object_from_url(url: str):
        model = get_model_from_url(url)
        pk = resolve(urlparse(url).path).kwargs.get('pk')
        if not pk:
            return None
        return model.objects.get(pk=pk)
    

    此设置序列化对象,以便content_object 字段包含指向相关对象的超链接,当使用此序列化程序 POST 到视图时,数据包含 content_object 键,我们获取相关对象并传下去。

    restore_fields 返回的 attrs 在 restore_object 方法中使用,由于我们查找内容对象并将其放入 attrs,restore_object 将 FinancialTransaction 对象上的 content_object 属性设置为检索到的对象然后 Django 负责其余的工作。

    到目前为止,我能看到的唯一缺点是这不会将 content_object 字段添加到可浏览的 API...但我不确定它是如何工作的,因为相关对象通常在选择,我认为我们不希望选择填充数据库中的每个对象。

    【讨论】:

      【解决方案2】:

      您可以在此文档中阅读更多内容:Marking extra actions for routing,如果您需要为您的POST 请求指定路由,请参阅他们的代码示例:

      from django.contrib.auth.models import User
      from rest_framework import status
      from rest_framework import viewsets
      # See these imports
      from rest_framework.decorators import detail_route, list_route
      from rest_framework.response import Response
      from myapp.serializers import UserSerializer, PasswordSerializer
      
      class UserViewSet(viewsets.ModelViewSet):
          """
          A viewset that provides the standard actions
          """
          queryset = User.objects.all()
          serializer_class = UserSerializer
      
          # just place your logical def with the existing decorator, like so:
          @detail_route(methods=['post'])
          def set_password(self, request, pk=None):
              user = self.get_object()
              serializer = PasswordSerializer(data=request.DATA)
              if serializer.is_valid():
                  user.set_password(serializer.data['password'])
                  user.save()
                  return Response({'status': 'password set'})
              else:
                  return Response(serializer.errors,
                                  status=status.HTTP_400_BAD_REQUEST)
      

      我希望这会有所帮助。

      【讨论】:

      • 感谢您,因为它让我深入研究了文档。感觉这个功能更多地属于序列化程序而不是视图,所以我figured out how to do that
      猜你喜欢
      • 2021-12-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-03-20
      • 1970-01-01
      • 1970-01-01
      • 2023-01-10
      • 1970-01-01
      相关资源
      最近更新 更多