2

所以我有相当于这个的模型(非常简化,显然):

class Mystery(models.Model):
    name = models.CharField(max_length=100)

class Character(models.Model):
    mystery = models.ForeignKey(Mystery, related_name="characters")
    required = models.BooleanField(default=True)

基本上,在每个谜团中都有许多角色,这些角色对故事来说可能是必不可少的,也可能不是。可以上演谜团的最少演员人数是该谜团所需的角色数量;最大数量是谜题的总字符数。

现在我正在尝试查询可以由给定数量的演员扮演的谜团。使用 Django 的过滤和注释功能的方式看起来很简单;毕竟,这两个查询都可以正常工作:

# Returns mystery objects with at least x characters in all
Mystery.objects.annotate(max_actors=Count('characters', distinct=True)).filter(max_actors__gte=x)

# Returns mystery objects with no more than x required characters
Mystery.objects.filter(characters__required=True).annotate(min_actors=Count('characters', distinct=True)).filter(min_actors__lte=x)

但是,当我尝试将两者结合起来时...

Mystery.objects.annotate(max_actors=Count('characters', distinct=True)).filter(characters__required=True).annotate(min_actors=Count('characters', distinct=True)).filter(min_actors__lte=x, max_actors__gte=x)

......它不起作用。min_actors 和 max_actors 都包含最大数量的参与者。正在运行的实际查询的相关部分如下所示:

SELECT `mysteries_mystery`.`id`,
    `mysteries_mystery`.`name`,
    COUNT(DISTINCT `mysteries_character`.`id`) AS `max_actors`,
    COUNT(DISTINCT `mysteries_character`.`id`) AS `min_actors`
FROM `mysteries_mystery`
    LEFT OUTER JOIN `mysteries_character` ON (`mysteries_mystery`.`id` = `mysteries_character`.`mystery_id`)
    INNER JOIN `mysteries_character` T5 ON (`mysteries_mystery`.`id` = T5.`mystery_id`)
WHERE T5.`required` = True
GROUP BY `mysteries_mystery`.`id`, `mysteries_mystery`.`name`

...这清楚地表明,虽然 Django 在字符表上创建第二个连接很好(表的第二个副本别名为 T5),但该表实际上并没有在任何地方使用,并且两个计数都在从非混叠版本中选择,显然两次都会产生相同的结果。

即使当我尝试使用extra子句从 T5 中进行选择时,我也被告知没有像 T5 这样的表,即使检查输出查询表明它仍然将第二个字符表别名为 T5。extra另一种使用子句的尝试是这样的:

Mystery.objects.annotate(max_actors=Count('characters', distinct=True)).extra(select={'min_actors': "SELECT COUNT(*) FROM mysteries_character WHERE required = True AND mystery_id = mysteries_mystery.id"}).extra(where=["`min_actors` <= %s", "`max_actors` >= %s"], params=[x, x])

但这不起作用,因为我不能在 WHERE 子句中使用计算字段,至少在 MySQL 上是这样。要是我能使用 HAVING 就好了,可惜 Django 的 .extra()不会也永远不会允许你设置 HAVING 参数。

有没有办法让 Django 的 ORM 做我想做的事?

4

2 回答 2

0

如何组合你的 Count()s:

Mystery.objects.annotate(max_actors=Count('characters', distinct=True),min_actors=Count('characters', distinct=True)).filter(characters__required=True).filter(min_actors__lte=x, max_actors__gte=x)

这似乎对我有用,但我没有用你的确切模型测试它。

于 2013-01-30T05:11:43.290 回答
0

已经有几个星期没有建议的解决方案了,所以对于其他可能正在寻找答案的人来说,这就是我最终的解决方法:

Mystery.objects.annotate(max_actors=Count('characters', distinct=True)).filter(max_actors__gte=x, id__in=Mystery.objects.filter(characters__required=True).annotate(min_actors=Count('characters', distinct=True)).filter(min_actors__lte=x).values('id'))

换句话说,过滤第一个计数和与第二个计数过滤的显式子查询中的 ID 匹配的 ID。有点笨重,但它对我的目的来说足够好。

于 2013-02-15T14:23:55.983 回答