Django REST框架在使用PrimaryKeyRelatedField时过度抓取



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表进行内部连接。

应该可以通过在预取字符时指定查询集来实现,并且only最初像这样获取主键:

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

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

最新更新