Django-Rest框架高效地检索反向外键上的相关字段

2024-05-28 23:31:58 发布

您现在位置:Python中文网/ 问答频道 /正文

我有以下模型,它们代表一个用户工作组。每个工作组都有一名领导和成员:

class WorkingGroup(models.Model):
    group_name = models.CharField(max_length=255)
    leader = models.ForeignKey(User, null=True, on_delete=models.SET_NULL)

class WorkingGroupMember(models.Model):
    group = models.ForeignKey(WorkingGroup, on_delete=models.CASCADE)
    user = models.ForeignKey(User, on_delete=models.CASCADE)

在DRF中,我希望高效地检索所有组(有几百个),作为以下json对象的数组:

^{pr2}$

为此,我设置了以下序列化程序:

class WorkingGroupSerializer(serializers.ModelSerializer):
    members = serializers.SerializerMethodField()
    class Meta:
        model = WorkingGroup
        fields = ('id', 'group_name', 'leader', 'members',)

    def get_members(self, obj):
        return obj.workinggroupmember_set.all().values_list('user_id', flat=True)

所以在我看来,我可以做些类似的事情:

groups = WorkingGroup.objects.all().prefetch_related('workinggroupmember_set')
group_serializer = WorkingGroupSerializer(groups, many=True)

这是可行的,并且给出了期望的结果,但是我发现它并没有很好地伸缩,因为预取workinggroupmember_set似乎没有在get_members方法内部使用(Silky显示了一个单独的查询来获取所有的WorkingGroup对象,然后在get_members方法中为每个workinggroupmember_set调用一个查询)。有没有一种方法可以在序列化程序中设置members字段,以便在不使用SerializerMethodField的情况下获取{}的平坦/单字段版本?或者其他方法让我正确地使用预取?在


Tags: 方法truegetmodelonmodelsgroupdelete
2条回答

这里的问题是您在all上执行values_list,这会使您的{}无效。当前无法使用values_list进行预取,请参见https://code.djangoproject.com/ticket/26565。您可以做的是将其转换为python代码而不是SQL

class WorkingGroupSerializer(serializers.ModelSerializer):
    members = serializers.SerializerMethodField()
    class Meta:
        model = WorkingGroup
        fields = ('id', 'group_name', 'leader', 'members',)

    def get_members(self, obj):
        return [wgm.user_id for wgm in obj.workinggroupmember_set.all()]

在最近一个使用drfv3.9.1和django 2.1的项目中,我需要递归地公开对象的所有子对象,方法是只与父对象直接连接,而父对象可能有多个子对象。在

以前,如果我要请求对象的“树”,我会得到:

{
    "uuid": "b85385c0e0a84785b6ca87ce50132659",
    "name": "a",
    "parent": null
}

通过应用下面所示的序列化,我得到:

^{pr2}$

以下是递归对象的models.py

class CategoryDefinition(BaseModelClass):
    name = models.CharField(max_length=100)
    parent = models.ForeignKey('self', related_name='children',
                               on_delete=models.CASCADE,
                               null=True, blank=True)

若要获取外键中的所有反向对象,请将字段应用于序列化程序类:

class DeepCategorySerializer(serializers.ModelSerializer):
    children = serializers.SerializerMethodField()

    class Meta:
        model = models.CategoryDefinition
        fields = '__all__'

    def get_children(self, obj):
        return [DeepCategorySerializer().to_representation(cat) for cat in obj.children.all()]

然后将此序列化程序应用于DRF视图函数或泛型类,例如:

re_path(r'categories/(?P<pk>[\w\d]{32})/',
        generics.RetrieveUpdateDestroyAPIView.as_view(
            queryset=models.CategoryDefinition.objects.all(),
            serializer_class=serializers.DeepCategorySerializer),
        name='category-update'),

相关问题 更多 >

    热门问题