如何在Django中`extra`调用提供的计算字段上过滤查询集?

时间:2009-11-04 15:55:03

标签: django django-models

这是我的django型号:

class Author (models.Model):
    name = models.CharField(max_length=255)
    removed = models.BooleanField(default=False)

class Image (models.Model):
    author = models.ForeignKey(Author)
    name = models.CharField(max_length=255)
    height = models.PositiveIntegerField()
    width  = models.PositiveIntegerField()

基本上,我需要选择每个未被移除的作者以及有5个或更少高度等于100的图像的作者。

我使用MySQL,这是版本信息:

  

mysql Ver 14.12 Distrib 5.0.67

当然,它看起来像这样:

Author.objects.filter(removed=False).extra(select={
    'imgcount': """SELECT COUNT(*) 
                   FROM ormtest_image 
                   WHERE height=100 AND 
                         ormtest_image.author_id=ormtest_author.id"""
}).filter(imgcount__lte=5)

它不起作用:“FieldError:无法将关键字'imgcount'解析为字段。选项包括:id,image,name,removed”

好的,让我们试试额外方法的论点:

Author.objects.filter(removed=False).extra(select={
    'imgcount': """SELECT COUNT(*) 
                   FROM ormtest_image 
                   WHERE height=100 AND 
                         ormtest_image.author_id=ormtest_author.id"""
}, where=['imgcount <= 5'])

它不起作用:“OperationalError:(1054,'where子句'中的未知列'imgcount')”,因为要过滤MySQL中计算字段的数据,你必须使用HAVING子句。

有什么想法吗?

我用Django 1.1和trunk的最新版本进行了测试。

到目前为止,我使用了这个黑客:

Author.objects.filter(removed=False).extra(select={
    'imgcount': """SELECT COUNT(*) 
                   FROM ormtest_image 
                   WHERE height=100 AND 
                         ormtest_image.author_id=ormtest_author.id"""
}, where=['1 HAVING imgcount <=5'])

P.S。 YAML夹具:

---
- model: ormtest.author
  pk: 1
  fields:
      name: 'Author #1'
      removed: 0
- model: ormtest.author
  pk: 2
  fields:
      name: 'Author #2'
      removed: 0
- model: ormtest.author
  pk: 3
  fields:
      name: 'Author #3'
      removed: 1
- model: ormtest.image
  pk: 1
  fields:
      author: 1
      name: 'Image #1'
      height: 100
      width: 100
- model: ormtest.image
  pk: 2
  fields:
      author: 1
      name: 'Image #2'
      height: 150
      width: 150
- model: ormtest.image
  pk: 3
  fields:
      author: 2
      name: 'Image #3'
      height: 150
      width: 100
- model: ormtest.image
  pk: 4
  fields:
      author: 2
      name: 'Image #4'
      height: 150
      width: 150

2 个答案:

答案 0 :(得分:2)

好的,未经测试,因为我没有您的数据 - 这是怎么回事:

Author.objects.filter(removed=False).select_related('image').filter(image__height=100).annotate(count_of_images=Count('image')).filter(count_of_images__lte=5)

编辑:

那几乎把你带走了。问题与外部联接有关......我认为这是应该为您完成的最终版本:

Author.objects.filter(removed=False).select_related('image').filter(Q(image__height=100) | Q(image__height__isnull=True)).annotate(count_of_images=Count('image')).filter(count_of_images__lte=5)

那里有Q(image__height=100) | Q(image__height__isnull=True)诀窍。它将获得具有高度为100 OR 作者的作者,其图像高度为null(意味着他们没有关联的图像)。

PS。谢谢你的问题......这实际上比我原先想象的更具挑战性,我在尝试测试过程中学到了一些很酷的技巧!


哎哟......我用sqlite3测试了我的最后一个解决方案。我没有用于测试的MySQL实例...: - (

让我思考另一种选择。

但是 - 是的 - 如果它在sqlite中工作,它应该在MySQL中工作;我会将其报告为错误。

答案 1 :(得分:0)

据我所知,你没有使用计数值,除了过滤那些大于1的作者。如果是这样,你可以用ORM代码完全用简单的查询来完成:

Author.objects.filter(removed=False, image__height__gte=100)