Skip to main content
 首页 » 编程设计

python之访问 DRF ListSerializer 中的特定实例

2025年05月04日23qq号

我目前有这个要序列化的 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 时,这会起作用,但是如果我想在 ListView 中序列化多个实例,并且我将一个查询集传递给序列化程序, DRF 在其上应用了一个 ListSerializer,现在 self.root.instance 是一个记录列表,我无法访问调用嵌套 RouteSerializer 的单个结果,因此我无法检索到正确的特征。

请您参考如下方法:

我跳进了 DRF 代码,终于明白是怎么回事了:

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

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

  1. 执行类似serializer = ResultSerializer(queryset, many=True)的调用
  2. 参数中有 many=True 会触发 BaseSerializer 的 many_init() 方法,这会创建一个 ResultSerializer 以查询集作为实例,所以 serializer.instance 是查询集。
  3. 接下来它创建一个扩展 ResultSerializer 的单个 ListSerializer 并且它的实例再次是查询集。

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

我最终解决这个问题的方法是重写 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