Django REST Frameworking избыточная выборка при использовании 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 будет получен только массив идентификаторов символов, так почему же он получает всю эту информацию? Я бы предположил, что он должен получить только это:
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")
иначе он делает один запрос на персонажа что может привести к тому, что представление будет делать сотни запросов, если у вас много квестов, в которых много персонажей.
Итак, мой вопрос: могу ли я упростить запрос для получения персонажей? В конце концов, я возвращаю только массив идентификаторов персонажей, так что не должно быть необходимости получать всех персонажей, объединенных с таблицей quests_quest_characters.
Это должно быть возможно, если указать набор запросов при предварительной выборке символов, и only
первоначально получить первичный ключ следующим образом:
Quest.objects.filter(
campaign_id=self.kwargs["campaign_id"]
).prefetch_related(
Prefetch(
"characters",
queryset=Character.objects.only('pk')
)
)
Обратите внимание, что это будет делать один запрос на символ, если для связанных символов используется любое другое поле, кроме pk
. В этом случае вы можете указать больше полей с помощью only
.