pyprism
pyprism

Reputation: 3008

After using prefetch_related still facing n+1

I am facing multiple duplicate db call even after I am using prefetch_related. Here is my views:

def index(request):
    product_qs = Product.objects.filter(shop_type='Grocery', featured=True, hide=False)
        product_list = product_qs.prefetch_related('image', 'size_list').order_by('ranking')
        products = terminator(request, product_list, 25)   // reusable paginator!
    context = {'categories': categories, 'products': products}
    return render(request, 'grocery/index.html', context)

Here is my models:

class ProductImage(models.Model):
    image = models.ImageField(upload_to='shop/product')
    color = models.BooleanField(default=False)
    image_thumbnail_index = ImageSpecField(source='image',
                                           processors=[ResizeToFill(308, 412)],
                                           format='JPEG',
                                           options={'quality': 100})
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

class Product(models.Model):
    shop_type = models.CharField(choices=shop_options, max_length=40)
    name = models.CharField(max_length=200, unique=True)
    image = models.ManyToManyField(ProductImage)
    category = TreeForeignKey('Category', null=True, blank=True, on_delete=models.PROTECT)
    slug = models.SlugField(max_length=200, db_index=True)
    featured = models.BooleanField(default=False)
    size = models.BooleanField(default=False)
    size_list = models.ManyToManyField(SizeList)
    available = models.BooleanField(default=True)  # out of stock ?
    hide = models.BooleanField(default=False)  # archive a product
    ranking = models.PositiveIntegerField()
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

and the template:

<figure class="product-image-container">
    <a href="#" class="product-image">
        <img src="{{ product.image.first.image_thumbnail_index.url }}" alt="{{ products.name }}">
    </a>
    <a href="{% url 'grocery_quickview' %}?product={{ product.name }}" class="btn-quickview">{% trans 'Quickview' %}</a>
</figure>
<div class="product-details">
    <h2 class="product-title">
        <a href="/grocery/product/{{ product.slug }}">
            {% if LANGUAGE_CODE == 'en' %}
                {{ product.name }}
            {% else %}
                {{ product.name_bn }}
            {% endif %}
        </a>
    </h2>
    <div class="price-box">
        <span class="product-price">
            {% if LANGUAGE_CODE == 'en' %}
                ৳ {{ product.price }}
            {% else %}
                ৳ {{ product.price_bn }}
            {% endif %}
        </span>
    </div><!-- End .price-box -->

Screenshot of sql calls: enter image description here

Upvotes: 1

Views: 98

Answers (1)

Bernhard Vallant
Bernhard Vallant

Reputation: 50786

Since you are already prefetching the ProductImage objects you may use the of the prefetched objects: {% with product.image.all|first as image %}{{ image.image_thumbnail_index.url }}(% endwith %}. (While calling first() does an extra query using LIMIT 1 at the end.)

Upvotes: 1

Related Questions