访问 DRF ListSerializer 中的特定实例
Posted
技术标签:
【中文标题】访问 DRF ListSerializer 中的特定实例【英文标题】:Accessing particular instances in a 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 的单个结果,因此无法检索正确的功能。
【问题讨论】:
【参考方案1】:我跳入 DRF 代码,终于明白发生了什么:
如果您只用serializer = ResultSerializer(result)
序列化一个实例,serializer.instance
只包含这个单一的、特定的result
实例,并且嵌套的序列化程序和字段可以使用self.root.instance
毫无问题地访问它。
现在,如果您序列化多个实例,就像默认的 list
操作一样,实际发生的情况如下:
-
像
serializer = ResultSerializer(queryset, many=True)
这样的调用被执行
在参数中包含many=True
会触发来自BaseSerializer
的many_init()
方法,这会创建一个以查询集作为实例的ResultSerializer
,因此serializer.instance
是查询集。
接下来,它创建一个扩展ResultSerializer
的ListSerializer
,它的实例又是查询集。
我错了认为ListSerializer
会为查询集中的每个元素创建单独的ResultSerializer
s。
我最终解决这个问题的方法是覆盖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
【讨论】:
以上是关于访问 DRF ListSerializer 中的特定实例的主要内容,如果未能解决你的问题,请参考以下文章