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

Python Django Rest框架-高效检索反向外键上的相关字段,python,django,performance,orm,django-rest-framework,Python,Django,Performance,Orm,Django Rest Framework,我有以下代表用户工作组的模型。每个工作组有一名组长和成员: 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.F

我有以下代表用户工作组的模型。每个工作组有一名组长和成员:

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对象的数组:

{
    'id': <the_group_id>
    'group_name': <the_group_name>
    'leader': <id_of_leader>
    'members': [<id_of_member_1>, <id_of_member_2>, ...]
}
因此,在我看来,我可以这样做:

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

这是可行的,并给出了期望的结果,但是我发现它的伸缩性并不好,因为预取
workinggroupmember\u set
似乎没有在
get\u members
方法中使用(Silky显示了一个获取所有
WorkingGroup
对象的查询,然后是
get\u members
方法中每个
workinggroupmember\u set
调用的查询)。是否有一种方法可以在序列化程序中设置
成员
字段,以便在不使用
SerializerMethodField
的情况下获取工作组成员集的扁平/单字段版本?或者有其他方法可以让我正确使用预回迁吗?

这里的问题是您正在执行的
值列表
f
all
使您的
prefetch\u related
无效。当前无法使用
值\u列表进行预取参见。您可以做的是将其转换为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()]

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

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

{
    "uuid": "b85385c0e0a84785b6ca87ce50132659",
    "name": "a",
    "parent": null
}
通过应用如下所示的序列化,我得到:

{
    "uuid": "b85385c0e0a84785b6ca87ce50132659",
    "name": "a",
    "parent": null
    "children": [
        {
            "uuid": "efd26a820b4e4f7c8e56c812a7791fcb",
            "name": "aa",
            "parent": "b85385c0e0a84785b6ca87ce50132659"
            "children": [
                {
                    "uuid": "ca2441fc7abf49b6aa1f3ebbc2dae251",
                    "name": "aaa",
                    "parent": "efd26a820b4e4f7c8e56c812a7791fcb"
                    "children": [],
                }
            ],
        },
        {
            "uuid": "40e09c85775d4f1a8578bba9c812df0e",
            "name": "ab",
            "parent": "b85385c0e0a84785b6ca87ce50132659"
            "children": [],
        }
    ],
}
下面是递归对象的
models.py

类类别定义(BaseModelClass):
name=models.CharField(最大长度=100)
parent=models.ForeignKey('self',related_name='children',
在_delete=models.CASCADE上,
空=真,空=真)
要获取外键中的所有反向对象,请将字段应用于序列化程序类:

class DeepCategorySerializer(serializers.ModelSerializer):
children=serializers.SerializerMethodField()
类元:
模型=模型。类别定义
字段='\uuuu所有\uuuu'
def get_子项(自身、obj):
将[DeepCategorySerializer().返回到对象children.all()中cat的_表示(cat)]
然后将此序列化程序应用于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'),
re_路径(r'categories/(?P[\w\d]{32})/,
generics.RetrieveUpdatedStroyapiView.as_视图(
queryset=models.CategoryDefinition.objects.all(),
serializer_class=serializers.DeepCategorySerializer),
name='category-update'),

你愿意改变你的模式吗?如果是的话,一个解决方案可能是完全摆脱
工作组成员
,并在
用户
中添加一个名为
成员
多个字段
,直接在
工作组
下。是的,这肯定是一种可能性。实际上,这种关系更像是一种关系-对于多个through,其中WorkingGroupMember具有应用程序/api其他区域中使用的其他字段,但对于此特定端点,我们只需要该组中的用户。A
ManyToManyField
将只创建一个透明的“through模型”这与您的
WorkingGroupMember
模型完全相同。您的
ManyToManyField
可能会使用
WorkingGroupMember
作为其直通模型,您会发现自己也处于相同的情况。太棒了!感谢您指出这个问题-我已经在DRF文档中查找了这不起作用的原因,结果是原来是django的问题。你的修复效果很好。
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'),