Во-первых, ты сам зачем-то выполняешь 3 запроса в каждом методе непонятно зачем.
def main_image(self):
return self.images.order_by('-is_main').first()
Сделает один запрос и вернёт ровно тоже самое. В одну строку
Во-вторых, в доке, кажется, достаточно подробно описано, как префетч кастомить:
qs = ProductImage.objects.order_by('-is_main')[:1]
products = Product.objects.prefetch_related(Prefetch('images', queryset=qs, to_attr='covers'))
products.covers.first()
Также можно изменить метод отдачи главной картинки, чтобы он мог работать с префетчеными изображениями
def main_image(self):
qs = self.images.order_by('-is_main')
if getattr(self, 'covers', None) is not None:
qs = self.covers
return qs.first()