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.

Вернуться на верх