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

Dmi*_*ylo 7 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()
Run Code Online (Sandbox Code Playgroud)

基本上,我需要选择每个未被删除的作者以及具有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)
Run Code Online (Sandbox Code Playgroud)

它不起作用:"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'])
Run Code Online (Sandbox Code Playgroud)

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

有任何想法吗?

我用Django 1.1和trunk的最新版本测试了这个.

到目前为止,我使用这个hack:

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'])
Run Code Online (Sandbox Code Playgroud)

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

cet*_*eek 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)
Run Code Online (Sandbox Code Playgroud)

编辑:

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

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

Q(image__height=100) | Q(image__height__isnull=True)里面就有窍门。它将获得图像高度为 100 的作者图像高度为 null 的作者(这意味着他们没有关联的图像)。

附言。谢谢你的问题...它实际上比我最初想象的更具挑战性,并且我在尝试测试它的过程中学到了一些很酷的技巧!


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

让我考虑一个替代方案。

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