【问题标题】:Accessing particular instances in a DRF ListSerializer访问 DRF ListSerializer 中的特定实例
【发布时间】:2017-10-01 02:23:59
【问题描述】:

我目前有这个想要序列化的 Django 模型:

class Result(models.Model):
    ...
    routes = models.ManyToManyField(Route)
    ...

class Route(models.Model):
    ...

class Feature(models.Model):
    result = models.ForeignKey(Result)
    route = models.ForeignKey(Route)
    description = models.TextField()

DRF 序列化器看起来像:

class ResultSerializer(serializers.ModelSerializer):
    ...
    route = RouteSerializer(many=True, required=False)
    ...

    class Meta:
        model = Result
        fields = '__all__'

class FeatureField(serializers.CharField):
    """
    Accepts text in the writes and looks up the correct feature for the reads.
    """

    def get_attribute(self, obj):
        # We pass the object instance onto `to_representation`, not just the field attribute.
        return obj

    def to_representation(self, obj):
        try:
            search_result = self.root.child.instance
            # FIXME: this is the problem.
            feature = Feature.objects.get(route=obj.id, search_result=search_result)
            feature = feature.description
        except Feature.DoesNotExist:
            feature = None
        return feature


class RouteSerializer(serializers.ModelSerializer):
    description = FeatureField(required=False)

    class Meta:
        model = Route
        fields = '__all__'

我在代码中的意思是,当我使用只有一个实例的 ResultSerializer 时,这可以工作,但是如果我想在列表视图中序列化多个实例,并且我将查询集传递给序列化器, DRF 在其上应用 ListSerializer,现在 self.root.instance 是记录列表,我无法访问调用嵌套 RouteSerializer 的单个结果,因此无法检索正确的功能。

【问题讨论】:

    标签: python django django-rest-framework


    【解决方案1】:

    我跳入 DRF 代码,终于明白发生了什么:

    如果您只用serializer = ResultSerializer(result) 序列化一个实例,serializer.instance 只包含这个单一的、特定的result 实例,并且嵌套的序列化程序和字段可以使用self.root.instance 毫无问题地访问它。

    现在,如果您序列化多个实例,就像默认的 list 操作一样,实际发生的情况如下:

    1. serializer = ResultSerializer(queryset, many=True) 这样的调用被执行
    2. 在参数中包含many=True 会触发来自BaseSerializermany_init() 方法,这会创建一个以查询集作为实例的ResultSerializer,因此serializer.instance 是查询集。
    3. 接下来,它创建一个扩展ResultSerializerListSerializer,它的实例又是查询集。

    我错了认为ListSerializer 会为查询集中的每个元素创建单独的ResultSerializers。

    我最终解决这个问题的方法是覆盖ResultSerializer.to_representation() 方法:

    class ResultSerializer(serializers.ModelSerializer):
        def to_representation(self, instance):
            # When we call Results with many=True, the serializer.instance is a list with several records,
            # we can't know which particular instance is spawning the nested serializers so we add it here.
            self._instance = instance
            return super(ResultSerializer, self).to_representation(instance)
    

    最后像这样在 FeatureField 中使用它:

    class FeatureField(serializers.CharField):
        """
        Accepts text in the writes and looks up the correct feature for the reads.
        """
    
        def get_attribute(self, obj):
            # We pass the object instance onto `to_representation`, not just the field attribute.
            return obj
    
        def to_representation(self, obj):
            # If the root is a ListSerializer, retrieve the right Result instance using the `_instance` attribute.
            try:
                if isinstance(self.root, serializers.ListSerializer):
                    search_result = self.root.child._instance
                else:
                    search_result = self.root.instance
                feature = Feature.objects.get(route=obj.id, search_result=search_result)
                feature = feature.pickup_instructions
            except Feature.DoesNotExist:
                feature = None
            return feature
    

    【讨论】:

      猜你喜欢
      • 2018-01-13
      • 1970-01-01
      • 1970-01-01
      • 2021-12-05
      • 1970-01-01
      • 2011-12-08
      • 2019-11-30
      • 2017-08-16
      • 2017-12-10
      相关资源
      最近更新 更多