访问DRF ListSerializer中的特定实例

时间:2017-05-03 10:01:46

标签: python django django-rest-framework

我目前有这个想要序列化的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的单个结果,因此无法检索正确的功能。

1 个答案:

答案 0 :(得分:3)

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

如果仅使用serializer = ResultSerializer(result)序列化一个实例,则serializer.instance仅包含此单个特定result实例,并且嵌套序列化程序和字段可以使用{{1 }}

现在,如果您序列化多个实例,例如默认的self.root.instance操作,那么实际发生的情况如下:

  1. 执行list之类的通话
  2. 在参数中使用serializer = ResultSerializer(queryset, many=True)会从many=True触发many_init()方法,这会创建一个以查询集为实例的BaseSerializer,因此ResultSerializer为查询集。
  3. 接下来它创建一个serializer.instance扩展ListSerializer,其实例再次是查询集。
  4. 我错了认为ResultSerializer会为查询集中的每个元素创建单独的ListSerializer

    我最终如何解决这个问题是重写ResultSerializer方法:

    ResultSerializer.to_representation()

    最后在FeatureField中使用它,如下所示:

    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)