Let's say I have a Product
model with products in a storefront, and a ProductImages
table with images of the product, which can have zero or more images. Here's a simplified example:
class Product(models.Model):
product_name = models.CharField(max_length=255)
# ...
class ProductImage(models.Model):
product = models.ForeignKey(Product, related_name='images')
image_file = models.CharField(max_length=255)
# ...
When displaying search results for products, I want to prioritize products which have images associated with them. I can easily get the number of images:
from django.db.models import Count
Product.objects.annotate(image_count=Count('images'))
But that's not actually what I want. I'd like to annotate it with a boolean field, have_images
, indicating whether the product has one or more images, so that I can sort by that:
Product.objects.annotate(have_images=(?????)).order_by('-have_images', 'product_name')
How can I do that? Thanks!
When you have to annotate existence with some filters,
Sum
annotation can be used. For example, following annotates if there are any GIFs inimages
:This will actually count them, but any pythonic
if product.animated_images:
will work same as it was boolean.Read the docs about extra
Tested it works
But
order_by
orwhere
(filter) by this field doesn't for me (Django 1.8) 0o:I have found https://code.djangoproject.com/ticket/19434 still opened.
So if you have such troubles like me, you can use raw
As from Django 1.11 it is possible to use
Exists
. Example below comes from Exists documentation:If performance matters, my suggestion is to add the
hasPictures
boolean field (aseditable=False
)Then keep right value through
ProductImage
model signals (or overwritingsave
anddelete
methods)Advantages:
Use conditional expressions and cast outputfield to BooleanField
I eventually found a way to do this using django 1.8's new conditional expressions:
And that's how I finally found incentive to upgrade to 1.8 from 1.7.