【问题标题】:Django REST Frameworking overfetching when using PrimaryKeyRelatedField使用 PrimaryKeyRelatedField 时 Django REST Frameworking 过度获取
【发布时间】:2021-11-19 02:39:30
【问题描述】:

我的 Django 模型:

class Quest(models.Model):
    name = models.CharField(max_length=255)
    is_completed = models.BooleanField(default=False)
    characters = models.ManyToManyField(Character, blank=True)
    campaign = models.ForeignKey(Campaign, on_delete=models.CASCADE, editable=False)

DRF 序列化器:

class QuestSerializer(serializers.ModelSerializer):
    characters = serializers.PrimaryKeyRelatedField(many=True, read_only=True)

    class Meta:
        model = Quest
        fields = "__all__"

我遇到的问题是,当我获取任务列表时,它会执行如下查询:

SELECT ("quests_quest_characters"."quest_id") AS
"_prefetch_related_val_quest_id", "characters_character"."id",
"characters_character"."legacy_id", "characters_character"."name",
"characters_character"."subtitle", "characters_character"."avatar",
"characters_character"."avatar_crop", "characters_character"."text",
"characters_character"."is_npc", "characters_character"."is_hidden",
"characters_character"."dm_notes",
"characters_character"."campaign_id",
"characters_character"."created_at",
"characters_character"."updated_at" FROM "characters_character" INNER
JOIN "quests_quest_characters" ON ("characters_character"."id" =
"quests_quest_characters"."character_id") WHERE
"quests_quest_characters"."quest_id" IN (1281, 1280, 1279, 1278, 1277,
1276, 1275, 1274, 1273, 1272, 1271, 1270, 1269, 1268, 1267, 1266,
1265, 1264, 1263, 1262, 1261, 1260, 1259, 1258, 1257, 1256, 1255,
1254)

这是一个包含很多字段的大查询,即使在 JSON 结果中只返回了一个字符 id 数组,那么它为什么要获取所有这些信息呢?它应该只是获取这个,我想:

SELECT "character_id" FROM "quests_quest_characters" WHERE "quest_id" IN (1281, 1280, 1279, 1278, 1277,
1276, 1275, 1274, 1273, 1272, 1271, 1270, 1269, 1268, 1267, 1266,
1265, 1264, 1263, 1262, 1261, 1260, 1259, 1258, 1257, 1256, 1255,
1254)

我的看法:

class QuestController(viewsets.ModelViewSet):
    serializer_class = QuestSerializer

    def get_queryset(self):
        return Quest.objects.filter(campaign_id=self.kwargs["campaign_id"]).prefetch_related("characters")
        
    def perform_create(self, serializer):
        return serializer.save(campaign_id=self.kwargs["campaign_id"])

我正在使用prefetch_related("characters"),否则它会每个字符执行一个查询,如果您有很多包含大量字符的任务,这可能会导致视图执行数百个查询。

所以我的问题是:我可以使获取字符的查询更简单吗?毕竟我只返回一个字符ID数组,所以应该不需要获取整个字符,内部加入了 quests_quest_characters 表。

【问题讨论】:

    标签: django django-models django-rest-framework django-queryset


    【解决方案1】:

    应该可以在预取字符时指定一个查询集,然后only最初像这样获取主键:

    Quest.objects.filter(
        campaign_id=self.kwargs["campaign_id"]
    ).prefetch_related(
        Prefetch(
            "characters",
            queryset=Character.objects.only('pk')
        )
    )
    

    请注意,如果为相关字符访问除pk 以外的任何其他字段,这将对每个字符执行一次查询。在这种情况下,您可以使用only 指定更多字段。

    【讨论】:

    • 谢谢!但这仍然是对字符表进行查询,内部与 quests_quest_characters 连接,对吗?这似乎仍然是一种浪费,因为字符 id 已经包含在该表中,但我想性能影响也可以忽略不计。
    • 是的。如果您使用自己的直通模型,似乎可以完成。否则它看起来像 django 内部的事情。
    • 如果没有更好的答案出现,我会按预期标记您的答案,即使这不是我希望的理想解决方案。但我认为你是对的,这里没什么可做的。
    猜你喜欢
    • 2021-11-23
    • 2017-10-13
    • 1970-01-01
    • 2016-08-13
    • 2013-07-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多