A
A
Andrey Salnikov2017-10-18 10:22:05
Django
Andrey Salnikov, 2017-10-18 10:22:05

How to use prefetch_related in model method?

There is a trace. models:

class Product(models.Model):
    name = models.CharField('Наименование', max_length=255)
    
    def main_image(self):
        first_image = self.images.filter(is_main=True).first()
        if first_image is not None:
            i = first_image
        elif self.images.first() is not None:
            i = self.images.first()
        else:
            i = None
        return i

class ProductImage(models.Model):
    product = models.ForeignKey(Product, on_delete=models.CASCADE, related_name='images')
    is_main = models.BooleanField(default=0, verbose_name='Основное')
    image = models.ImageField(upload_to='images', verbose_name='Изображение')

When I call the main_image method, it works fine, but generates 3-4 calls to the database. And when I use it in a template with 15 products, the number of requests increases proportionally.
The question is, can this be optimized somehow? I read about prefetch_related, but I just can't use it inside the method.

Answer the question

In order to leave comments, you need to log in

1 answer(s)
R
Roman Kitaev, 2017-10-18
@Shshzik

Firstly, for some reason you yourself perform 3 requests in each method, it is not clear why.

def main_image(self):
    return self.images.order_by('-is_main').first()

It will make one request and return exactly the same thing. In one line
Secondly, the doc seems to describe in sufficient detail how to customize the prefetch:
qs = ProductImage.objects.order_by('-is_main')[:1]
products = Product.objects.prefetch_related(Prefetch('images', queryset=qs, to_attr='covers'))
products.covers.first()

You can also change the main image rendering method so that it can work with prefetched images.
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()

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question