Django使用布尔字段查询注释

Bre*_*ser 32 python django orm django-queryset

假设我Product在店面中有一个带有产品的模型,以及一个ProductImages包含产品图像的表格,其中可以包含零个或多个图像.这是一个简化的例子:

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)
  # ...
Run Code Online (Sandbox Code Playgroud)

在显示产品的搜索结果时,我想优先考虑具有与之关联的图像的产品.我可以轻松获得图像数量:

from django.db.models import Count
Product.objects.annotate(image_count=Count('images'))
Run Code Online (Sandbox Code Playgroud)

但这实际上并不是我想要的.我想用布尔字段对它进行注释have_images,指示产品是否有一个或多个图像,以便我可以按以下方式排序:

Product.objects.annotate(have_images=(?????)).order_by('-have_images', 'product_name')
Run Code Online (Sandbox Code Playgroud)

我怎样才能做到这一点?谢谢!

Bre*_*ser 48


我最终使用django 1.8的新条件表达式找到了一种方法:

from django.db.models import Case, When, Value, IntegerField
q = (
    Product.objects
           .filter(...)
           .annotate(image_count=Count('images'))
           .annotate(
               have_images=Case(
                   When(image_count__gt=0,
                        then=Value(1)),
                   default=Value(0),
                   output_field=IntegerField()))
           .order_by('-have_images')
)
Run Code Online (Sandbox Code Playgroud)

这就是我终于找到从1.7升级到1.8的动力.


pro*_*oxy 14

从 Django 1.11 开始,可以使用Exists. 下面的示例来自Exists 文档

>>> from django.db.models import Exists, OuterRef
>>> from datetime import timedelta
>>> from django.utils import timezone
>>> one_day_ago = timezone.now() - timedelta(days=1)
>>> recent_comments = Comment.objects.filter(
...     post=OuterRef('pk'),
...     created_at__gte=one_day_ago,
... )
>>> Post.objects.annotate(recent_comment=Exists(recent_comments))
Run Code Online (Sandbox Code Playgroud)

  • 我不接受几年前的回答,并接受了这个答案。Django 1.11 在当时还不算什么,但肯定没有人应该再使用 Django 1.8。 (2认同)

Nou*_*pra 7

使用条件表达式并将 outputfield 转换为 BooleanField

Product.objects.annotate(image_count=Count('images')).annotate(has_image=Case(When(image_count=0, then=Value(False)), default=Value(True), output_field=BooleanField())).order_by('-has_image')
Run Code Online (Sandbox Code Playgroud)