After using prefetch_related still facing n+1 - django

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>
{% trans 'Quickview' %}
</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:

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.)

Related

Django Change boolean field with click

I am working on a To-do app. The individual to-dos reference a to-do list via a foreign key and the to-do lists reference a project via a foreign key.
I want the to-do's status to be set to true when clicked. I have seen some tutorials where this is done but I haven't been able to get this to work yet.
Here are the models:
class Project(models.Model):
title = models.CharField(max_length= 200)
description = tinymce_models.HTMLField()
status = models.CharField(max_length=20, choices=PROJECT_CHOICES, default="active")
date = models.DateTimeField(auto_now_add=True, null=True)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse ('project_detail', args=[str(self.id)])
class ProjectTodoGroup(models.Model):
title = models.CharField(max_length=250)
description = tinymce_models.HTMLField()
project = models.ForeignKey(Project, blank=True, on_delete=models.CASCADE, related_name='todo_group')
date = models.DateTimeField(auto_now_add=True, null=True)
def __str__(self):
return self.title
class ProjectTodo(models.Model):
title = models.CharField(max_length= 250)
notes = tinymce_models.HTMLField()
status = models.BooleanField(default=False)
projectgroup = models.ForeignKey(ProjectTodoGroup, blank=True, null=True, on_delete=models.CASCADE, related_name='todo_set')
date = models.DateTimeField(auto_now_add=True, null=True)
def __str__(self):
return self.title
The view:
model = ProjectTodo
fields = ['status']
def get_context_data(self, **kwargs):
# Call the base implementation first to get a context
context = super().get_context_data(**kwargs)
context['project'] = get_object_or_404(Project, id=self.kwargs.get('pk'))
return context
def get_success_url(self):
return reverse('company_project:todo_group_detail', args=[self.kwargs.get('pk'), (self.object.id)])
Everything I have tried to far with the view hasn't worked.
The template:
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% block content %}
<div class="section-container container">
<div class="todo-group">
<h2>{{ projecttodogroup.title }}</h2>
<p>{{ projecttodogroup.description | safe }}</p>
</div>
<div><b>Todos</b></div>
{% if projecttodogroup.todo_set.all %}
{% for todo in projecttodogroup.todo_set.all %}
<div class="todos" style="padding: 10px;">
{{ todo.title }}
</div>
{% endfor %}
{% else %}
<p>No Todos have been have been added yet.</p>
{% endif %}
<h1>Add Todo</h1>
<form action="" method="post">
{% csrf_token %}
{{ form.media }}
{{ form|crispy }}
<input type="submit" value="save">
</form>
</div>
{% endblock content %}

Django queryset Product model and Product_images model, TemplateSyntaxError

I am developing an ecommerce website with Django. I had Product and Product_images models as below:
class Product(models.Model):
tags = models.ManyToManyField(Tag, related_name='products')
same_product = models.ManyToManyField('self', related_name='same_products', blank=True)
category = models.ForeignKey('Category', on_delete=models.CASCADE, related_name='product_categories')
who_like = models.ManyToManyField(User, related_name='liked_products', blank=True)
title = models.CharField('Title', max_length=100, db_index=True)
slug = models.SlugField('Slug', max_length=110, unique = True)
sku = models.CharField('SKU', max_length=50, db_index=True)
description = models.TextField('Description', null=True, blank=True)
sale_count = models.IntegerField('Sale Count', default=0)
is_new = models.BooleanField('is_new', default=True)
is_featured = models.BooleanField('is_featured', default=False)
is_discount = models.BooleanField('is_discount', default=False)
price = models.DecimalField('Price', max_digits=7, decimal_places=2)
discount_value = models.IntegerField('Discount Value', null=True, blank=True)
def __str__(self):
return self.title
class Product_images(models.Model):
# relations
product = models.ForeignKey(Product, on_delete=models.CASCADE, related_name='images')
# informations
image = models.ImageField('Image', upload_to='media/product_images')
is_main = models.BooleanField('Main Image', default=False)
is_second_main = models.BooleanField('Second Main Image', default=False)
# moderations
status = models.BooleanField('Status', default=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
db_table = 'image'
verbose_name = 'Image'
verbose_name_plural = 'Images'
ordering = ('created_at',)
def __str__(self):
return f'{self.image}'
In my Product_images model I store several images for one Product, in Product_images model I wrote
boolean fields with names is_main and is_second_main. In my template I want to get these images, in my terminal (interactive shell) when I write single_product.images.get(is_main=True).image.url can get image url, but in template I can't get image, but get an error as below:
TemplateSyntaxError at /
Could not parse the remainder: '(is_main=True).image.url' from 'product.images.get(is_main=True).image.url'
Below is my view and template:
def index(request):
products = Product.objects.all()
context = {
'products': products
}
return render(request, 'index/index.html', context)
{% for product in products %}
<div class="front">
<a href="product-page(no-sidebar).html">
<img src="{{ product.images.get(is_main=True).image.url }}" class="img-fluid blur-up lazyload bg-img" alt="">
</a>
</div>
<div class="back">
<a href="product-page(no-sidebar).html">
<img src="{{ product.images.get(is_main=True).image.url }}" class="img-fluid blur-up lazyload bg-img" alt="">
</a>
</div>
{% endfor %}
Please, help me with this problem and properly display these images, thanks in advance.
I already solve this problem, maybe it is not the best way, but work.
{% for product in products %}
<div class="product-box">
<div class="img-wrapper">
<div class="front">
<a href="{% url 'product-detail' product.slug %}">
{% for image in product.images.all %}
{% if image.is_main %}
<img src="{{ image.image.url }}" class="img-fluid blur-up lazyload bg-img" alt="">
{% endif %}
{% endfor %}
</a>
</div>
<div class="back">
<a href="{% url 'product-detail' product.slug %}">
{% for image in product.images.all %}
{% if image.is_second_main %}
<img src="{{ image.image.url }}" class="img-fluid blur-up lazyload bg-img" alt="">
{% endif %}
{% endfor %}
</a>
</div>
{% endfor %}

Django template loop through items with parent ID or PK

I'm trying to set up magnific popup on django.
My goal is to have one main picture in the homepage overview gallery view, which when clicked, would open a popup with the related images from the same photoshoot i.e. images with the same ID or PK.
I tried to apply the following approach
but i just cannot get it to work, maybe someone could help me out in this
My models.py
class Item(models.Model):
name = models.CharField(blank=False, max_length=200)
category = models.ForeignKey(Category, blank=True, null=True, on_delete=models.SET_NULL)
order = models.IntegerField(blank=True, null=True)
active = models.BooleanField(blank=True, default=False)
objects = models.Manager()
class Meta:
verbose_name_plural = 'items'
def __str__(self):
return self.name
class ItemImage(models.Model):
image = ProcessedImageField(
blank=True,
null=True,
processors=[ResizeToFit(width=1680, upscale=False)],
format='JPEG',
options={'quality':90})
order = models.IntegerField(blank=True, null=True)
main = models.BooleanField(blank=True, default=False)
cover = models.BooleanField(blank=True, default=False)
item = models.ForeignKey(Item, related_name='items', blank=True, null=True, on_delete=models.SET_NULL)
objects = models.Manager()
class Meta:
verbose_name_plural = 'item images'
Views.py
def portraits(request):
port = ItemImage.objects.filter(item__category__slug='portraits', item__active=True, main=True,).order_by('item__order')
portall = ItemImage.objects.filter(item__category__slug='portraits', item__active=True).order_by('item__order')
context = {
'main_portraits': port,
'all_portraits': portall
}
return render(request, 'gallery/portraits.html', context)
and Template:
{% block content %}
<div class="grid">
{% for pic in main_portraits %}
<div class="item">
<div class="item">
<div class="outer-text">
<div class="text">
{{ pic.item.name }}
<p>Click to view gallery</p>
</div>
</div>
<a><img class="lazy" alt=""
sizes="(min-width:1400px) 1220px
(min-width:1000px) 1000px,
(min-width:500px) 700px,
(min-width:320px) 420px,
280px"
srcset="{{ pic.image_xs.url }} 280w,
{{ pic.image_s.url }} 420w,
{{ pic.image_m.url }} 700w,
{{ pic.image_l.url }} 1000w,
{{ pic.image_xl.url }} 1220w" />
</a> {{ pic.item.pk }}
</div>
<div class="lazy">
{% for p in all_portraits %}
{% endfor %}
</div>
</div>
{% endfor %}
</div>
{% endblock %}
I have set
z.item.pk
just as a test, to see if any of my manipulations result in the pk's to bunch up. For example the first for-loop returns a picture with PK "24", I need for the second for-lop to return only images with the same PK; and so for every image. I think the answer might be connected with _set.all function, just like in the related question above, but I cant seem to get it to work in my case. Feels like I'm missing something here.
current output:
<div class="grid">
<div class="item">
<div class="item">
<div class="outer-text">
<div class="text">
Palagā tītā
<p>Click to view gallery</p>
</div>
</div>
<a><img class="lazy" alt=""
sizes="(min-width:1400px) 1220px
(min-width:1000px) 1000px,
(min-width:500px) 700px,
(min-width:320px) 420px,
280px"
srcset="/media/CACHE/images/IMG_8329_3Vi8mYO_GD621ql/958ba5dbee5efe28fd2f5054b8f819e1.jpg 280w,
/media/CACHE/images/IMG_8329_3Vi8mYO_GD621ql/02d12ca7f0633fee2fc762cf96f7889e.jpg 420w,
/media/CACHE/images/IMG_8329_3Vi8mYO_GD621ql/ba5fa6633e92a288e3b2f47a713d64c2.jpg 700w,
/media/CACHE/images/IMG_8329_3Vi8mYO_GD621ql/fe0d559fef5b02434c43f841005d4961.jpg 1000w,
/media/CACHE/images/IMG_8329_3Vi8mYO_GD621ql/96d0e52dff14d1bc4b60bbec674565db.jpg 1220w" />
</a> 24
</div>
<div class="lazy">
</div>
</div>
You need prefiltered querysets containing the related images for every main image before handing over to the template.
def portraits(request):
ports = ItemImage.objects.filter(item__category__slug='portraits', item__active=True, main=True,).order_by('item__order')
for p in ports:
# You may not need the item__category__slug filter
# if there are only images of the same category
# associated with an item.
# Also, if you want to exclude the main image
# from the set of related images, you need to add the filter
# main=False
p.related_images = ItemImage.objects.filter(item__category__slug='portraits', item__id=p.item.id)
context = {
'main_portraits': ports,
}
return render(request, 'gallery/portraits.html', context)
Then you can loop over main_portraits in the template, and get the related images for each main image in a nested loop:
{% for mainp in main_portraits %}
{% for im in mainp.related_images %}
{# do something with the related images #}
{% endfor %}
{% endfor %}
You can break down the models like this it will make the querying easier.
# models.py
class Item(mdoels.Model):
name = models.CharField(blank=False, max_length=200)
category = models.ForeignKey(Category, blank=True, null=True, on_delete=models.SET_NULL)
...
display_image = models.ProcessedImageField(...)
class ItemImage(models.Model):
item = models.ForeignKey(Item, related_name='images', blank=True, null=True, on_delete=models.SET_NULL)
image = models.ProcessedImageField(...)
...
#views.py
def portraits(request):
items = Item.objects.filter(category__slug='portraits', active=True)
return render(request, 'gallery/portraits.html', context={items: items})
#template
{% for item in items %}
<h1> {{item.name}} </h1>
<img src={{item.display_image}} />
{% for item_image in item.images.all %}
<img src={{item_image.image}} />
{% endfor %}
{% endfor %}

Displaying images "not featured" with featured images in Django template language

I am trying to display small thumbnail under featured images using django template language but for some reason main image displays fine but not small image. Idea is to display one featured image and rest as "not featured" images.
My single-page code to display "Featured Images" with "Not Featured" images is given below.
{% extends 'base.html' %}
{% block content %}
<h1>{{ product.title }}</h1>
{% for img in images %}
{% if img.featured %}
<h1>Featured</h1>
<img class='img-responsive' src="{{ MEDIA_URL }}{{ img.image }}"/>
{% else %}
<div class="col-xs-6 col-md-3">
<a href="#" class="thumbnail">
<img class='img-responsive' src="{{ MEDIA_URL }}{{ img.image }}"/>
</a>
</div>
{% endif %}
{% endfor %}
{% endblock %}
Please advise.
My models.py is....
from django.core.urlresolvers import reverse
from django.db import models
class Product(models.Model):
title = models.CharField(max_length=120)
description = models.TextField(null=True, blank=True)
price = models.DecimalField(decimal_places=2, max_digits=100, default=29.99)
sale_price = models.DecimalField(decimal_places=2, max_digits=100, null=True, blank=True)
slug = models.SlugField(unique=True)
timestamp = models.DateTimeField(auto_now_add=True, auto_now=False)
updated = models.DateTimeField(auto_now_add=False, auto_now=True)
active = models.BooleanField(default=True)
def __unicode__(self):
return str(self.title)
class Meta:
unique_together = ('title', 'slug')
def get_price(self):
return self.price
def get_absolute_url(self):
return reverse('single_product', kwargs={'slug': self.slug})
class ProductImage(models.Model):
product = models.ForeignKey(Product)
image = models.ImageField(upload_to='products/images/')
featured = models.BooleanField(default=False)
thumbnail = models.BooleanField(default=False)
active = models.BooleanField(default=True)
updated = models.DateTimeField(auto_now_add=False, auto_now=True)
def __unicode__(self):
return self.product.title
All the images were set as featured and that was causing issue. Thanks for help #f43d65. Also consulted project on Github at: https://github.com/codingforentrepreneurs/ecommerce/blob/master/ecommerce/templates/products/single.html.
Try to replace
{{ MEDIA_URL }}{{ img.image }}
to
{{ img.image.url }}

Get Featured Image from different Model

I have 2 model objects, Business & BusinessImage as so, listed with views and index.html. I am trying to list the business's featured image, but it's not happening. I am getting the following error:
'QuerySet' object has no attribute 'businessimage_set'
How can I get the business featured image for a list?
Business
class Business(models.Model):
name = models.CharField("Name", max_length=70, default="Business Name")
slug = models.SlugField()
description = models.TextField("About", max_length=400)
category = models.ManyToManyField(Category, verbose_name="Categories", blank=True)
order = models.IntegerField("Order", default=0)
claimed = models.BooleanField("Claimed", default=False)
featured = models.BooleanField("Featured", default=False)
class Meta:
ordering = ['order']
verbose_name = "Business"
verbose_name_plural = "Businesses"
def __str__(self):
return self.name
BusinessImage
class BusinessImage(models.Model):
business = models.ForeignKey(Business)
image = models.ImageField(upload_to="images/business")
title = models.CharField(max_length=120)
featured = models.BooleanField(default=False)
timestamp = models.DateTimeField(auto_now_add=True, auto_now=False)
updated = models.DateTimeField(auto_now_add=False, auto_now=True)
def __str__(self):
return self.title
view.py
from .models import Business, BusinessImage
def index_view(request):
latest_business_list = Business.objects.all()
images = latest_business_list.businessimage_set.all()
template = loader.get_template('index.html')
context = RequestContext(request, {
'latest_business_list': latest_business_list,
'images': images,
})
return HttpResponse(template.render(context))
index.html
{% block content %}
<div class="text-center business_title">
<h2>Featured</h2>
</div>
{% if latest_business_list %}
{% for business in latest_business_list|slice:":4" %}
{% if business.active %}
<div class="col-sm-6 col-md-3">
<li>{{ business.name }}</li>
{% for image in latest_business_list.businessimage_set.all %}
{% if image.featured %}
<a href="{% url 'single_product' product.slug %}">
<img src="{{MEDIA_URL}}{{image.image}}" alt="{{image}}">
</a>
{% endif %}
{% endfor %}
</div>
{% endif %}
{% endfor %}
{% endif %}
{% endblock %}
businessimage_set is an attribute of a Business instance, but you're trying to access it as an attribute of a queryset (i.e. list of businesses). If your goal is just to be able to access the images for each business in a template, you can leave out images entirely. Instead your template would have:
{% for image in business.businessimage_set.all %}
(Though look into prefetch_related for efficiency.)