访问DRF ListSerializer中的特定实例

Jav*_*ero 1 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()
Run Code Online (Sandbox Code Playgroud)

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__'
Run Code Online (Sandbox Code Playgroud)

我在代码中表示的问题是,当我仅使用一个实例使用ResultSerializer时,此方法有效,但是例如,如果要在列表视图中序列化多个实例,并将查询集传递给序列化程序,DRF将应用ListSerializer位于它的顶部,现在self.root.instance是记录的列表,我无法访问调用嵌套RouteSerializer的单个结果,因此无法检索正确的Feature。

Jav*_*ero 5

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

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

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

  1. 就像一个呼叫serializer = ResultSerializer(queryset, many=True)进行
  2. 具有many=True在参数触发many_init()从方法BaseSerializer,并且这产生了单个ResultSerializer与查询集作为实例,因此serializer.instance是查询集。
  3. 接下来,它创建一个ListSerializer扩展ResultSerializer,其实例再次是queryset。

我弄错的是认为ListSerializerResultSerializer为queryset中的每个元素创建单独的。

我最终解决的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)
Run Code Online (Sandbox Code Playgroud)

最后像下面这样在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
Run Code Online (Sandbox Code Playgroud)