Django使用布尔字段查询注释

时间:2015-07-01 18:36:55

标签: 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)
  # ...

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

from django.db.models import Count
Product.objects.annotate(image_count=Count('images'))

但那并不是我想要的。我想用布尔字段have_images对其进行注释,以指示产品是否有一个或多个图像,以便我可以对其进行排序:

Product.objects.annotate(have_images=(?????)).order_by('-have_images', 'product_name')

我该怎么做?谢谢!

6 个答案:

答案 0 :(得分:42)


我最终找到了一种方法,使用django 1.8的新conditional expressions

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

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

答案 1 :(得分:2)

使用条件表达式并将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')

答案 2 :(得分:1)

阅读有关extra

的文档
qs = Product.objects.extra(select={'has_images': 'CASE WHEN images IS NOT NULL THEN 1 ELSE 0 END' })

经测试可行

但是这个字段的order_bywhere(过滤器​​)不适合我(Django 1.8)0o:

  

如果您需要使用一些新的命令来生成查询集   通过extra()包含的字段或表使用order_by   参数到extra()并传入一系列字符串。这些字符串   应该是模型字段(如在正常的order_by()方法中)   querysets),形式为table_name.column_name或a的别名   您在select参数中指定的列为extra()。

qs = qs.extra(order_by = ['-has_images'])

qs = qs.extra(where = ['has_images=1'])
  

FieldError:无法将关键字'has_images'解析为字段。

我发现https://code.djangoproject.com/ticket/19434仍然打开。

因此,如果您遇到像我这样的麻烦,您可以使用raw

答案 3 :(得分:1)

如果性能很重要,我建议添加hasPictures布尔字段(editable=False

然后通过ProductImage model signals(或覆盖savedelete方法)保持正确的价值

优点:

  • 指数友好。
  • 更好的表现。避免加入。
  • 数据库不可知。
  • 编码它会将你的django技能提升到一个新的水平。

答案 4 :(得分:1)

从Django 1.11开始,可以使用Exists。以下示例来自Exists documentation

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

答案 5 :(得分:0)

当您必须使用某些过滤器注释存在时,可以使用Sum注释。例如,在注释images中是否有任何GIF:

Product.objects.filter(
).annotate(
    animated_images=Sum(
        Case(
             When(images__image_file__endswith='gif', then=Value(1)),
             default=Value(0),
             output_field=IntegerField()
        )
    )
)

这实际上会计算它们,但任何pythonic if product.animated_images:都将与布尔值相同。