Проблема с префетчем, связанным с n+1, в django. Как я могу ее решить?

У меня есть три модели с именами 'Route', 'Place', 'PlaceImage'.

class Route(models.Model):
    place = models.ForeignKey(Place, on_delete=models.CASCADE, null=False, blank=False)
    day = models.IntegerField(null=False, blank=False)
    order = models.IntegerField(null=False, blank=False)

class Place(models.Model):
    name = models.CharField(null=False, blank=False)


class PlaceImage(models.Model):
    place = models.ForeignKey(Place, on_delete=models.CASCADE, null=False, blank=False)
    image = models.ImageField(null=False, blank=False)

Я хочу отображать маршруты и размещать изображения на основе модели маршрута. Оригинальный код был...

# views.py
response_obj = (Route.objects.filter(day=day).prefetch_related("place__image").order_by('order'))
serializer = RouteSerializer(response_obj, many=True, context={'request': request})

# Part of RouteSerializer in serializers.py
placeImage = serializers.SerializerMethodField("get_placeImage_prefetch_related")
def get_placeImage_prefetch_related(self, group):
   request = self.context.get('request')
   image = group.place.image
   return request.build_absolute_uri(image.first().image.url)

И у меня есть N+1 запрос на получение изображения в таком виде

(0.000) SELECT "PLACE_IMAGE"."id", "PLACE_IMAGE"."place_id", "PLACE_IMAGE"."image", "PLACE_IMAGE"."created_at" FROM "PLACE_IMAGE" WHERE "PLACE_IMAGE"."place_id" = 80 ORDER BY "PLACE_IMAGE"."id" ASC LIMIT 1; args=(80,); alias=default
(0.000) SELECT "PLACE_IMAGE"."id", "PLACE_IMAGE"."place_id", "PLACE_IMAGE"."image", "PLACE_IMAGE"."created_at" FROM "PLACE_IMAGE" WHERE "PLACE_IMAGE"."place_id" = 79 ORDER BY "PLACE_IMAGE"."id" ASC LIMIT 1; args=(79,); alias=default
(0.000) SELECT "PLACE_IMAGE"."id", "PLACE_IMAGE"."place_id", "PLACE_IMAGE"."image", "PLACE_IMAGE"."created_at" FROM "PLACE_IMAGE" WHERE "PLACE_IMAGE"."place_id" = 78 ORDER BY "PLACE_IMAGE"."id" ASC LIMIT 1; args=(78,); alias=default

Как решить задачу n+1?

В файле views.py я попробовал prefetch_related(Prefetch('place__image', queryset=PlaceImage.objects.all().only('image'))) но возникла ошибка.

AttributeError: 'PlaceImage' object has no attribute '_add_hints'

Проблема заключается в .first(), вместо него можно использовать [0]:

def get_placeImage_prefetch_related(self, group):
    request = self.context.get('request')
    image = group.place.image
    return request.build_absolute_uri(image[0].image.url)

Возможно, такого изображения не существует. В этом случае вы будете работать с:

def get_placeImage_prefetch_related(self, group):
    request = self.context.get('request')
    try:
        image = group.place.image[0]
        return request.build_absolute_uri(image.image.url)
    except KeyError:
        pass  # return None
Вернуться на верх