我有两个模型,Item
和 ItemGroup
:
class ItemGroup(models.Model):
group_name = models.CharField(max_length=50)
# fields..
class Item(models.Model):
item_name = models.CharField(max_length=50)
item_group = models.ForeignKey(ItemGroup, on_delete=models.CASCADE)
# other fields..
我想编写一个序列化程序,它将获取所有项目组及其项目列表作为嵌套数组。
所以我想要这个输出:
[ {group_name: "item group name", "items": [... list of items ..] }, ... ]
如我所见,我应该使用 django rest 框架来编写:
class ItemGroupSerializer(serializers.ModelSerializer):
class Meta:
model = ItemGroup
fields = ('item_set', 'group_name')
意味着,我必须为 ItemGroup
(不是为 Item
)编写一个序列化程序。
为了避免很多查询,我传递了这个查询集:
ItemGroup.objects.filter(**filters).prefetch_related('item_set')
我看到的问题是,对于大型数据集,prefetch_related
会导致带有非常大的 sql IN
子句的额外查询,我可以通过查询避免这种情况在 Item 对象上:
Item.objects.filter(**filters).select_related('item_group')
这会导致更好的 JOIN。
是否可以查询 Item
而不是 ItemGroup
,并且还具有相同的序列化输出?
最佳答案
使用 prefetch_related
您将有两个查询 + 大 IN 子句问题,尽管它已被证明并且可移植。
我会根据您的字段名称给出一个更多示例的解决方案。它将创建一个函数,该函数使用您的 select_related
queryset
从 Item
的序列化程序进行转换。它将覆盖 View 的列表函数,并将一个序列化程序数据转换为另一个序列化程序数据,从而为您提供所需的表示形式。它将仅使用一个查询并解析结果将在 O(n)
中,因此它应该很快。
您可能需要重构 get_data
以便向结果中添加更多字段。
class ItemSerializer(serializers.ModelSerializer):
group_name = serializers.CharField(source='item_group.group_name')
class Meta:
model = Item
fields = ('item_name', 'group_name')
class ItemGSerializer(serializers.Serializer):
group_name = serializers.CharField(max_length=50)
items = serializers.ListField(child=serializers.CharField(max_length=50))
在 View 中:
class ItemGroupViewSet(viewsets.ModelViewSet):
model = models.Item
serializer_class = serializers.ItemSerializer
queryset = models.Item.objects.select_related('item_group').all()
def list(self, request, *args, **kwargs):
queryset = self.filter_queryset(self.get_queryset())
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True)
data = self.get_data(serializer.data)
s = serializers.ItemGSerializer(data, many=True)
return self.get_paginated_response(s.data)
serializer = self.get_serializer(queryset, many=True)
data = self.get_data(serializer.data)
s = serializers.ItemGSerializer(data, many=True)
return Response(s.data)
@staticmethod
def get_data(data):
result, current_group = [], None
for elem in data:
if current_group is None:
current_group = {'group_name': elem['group_name'], 'items': [elem['item_name']]}
else:
if elem['group_name'] == current_group['group_name']:
current_group['items'].append(elem['item_name'])
else:
result.append(current_group)
current_group = {'group_name': elem['group_name'], 'items': [elem['item_name']]}
if current_group is not None:
result.append(current_group)
return result
这是我用假数据得到的结果:
[{
"group_name": "group #2",
"items": [
"first item",
"2 item",
"3 item"
]
},
{
"group_name": "group #1",
"items": [
"g1 #1",
"g1 #2",
"g1 #3"
]
}]
关于python - django rest framework - 向后序列化以避免 prefetch_related,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53874174/