Django聚合和查询

时间:2010-07-01 20:49:59

标签: django django-queryset

我有许多可以是一种或多种类型的元素。

class Type(models.Model):
    name = models.CharField(max_length=128, unique=True)

class Element(models.Model):
    name = models.CharField(max_length=128, unique=True)
    type = models.ManyToManyField('Type')

假设我有3种类型和3种元素:

In [3]: Type.objects.all()
Out[3]: [<Type: His Type>, <Type: My Type>, <Type: Your Type>]

In [4]: [(e,e.type.all()) for e in Element.objects.all()]
Out[4]: 
[(<Element: First Element>, [<Type: My Type>]),
(<Element: Second Element>, [<Type: Your Type>]),
(<Element: Third Element>,
  [<Type: My Type>, <Type: Your Type>, <Type: His Type>])]

我正在尝试使用仅属于“My Type”类型的元素的查询集

我的想法是获取此类型的元素,并检查它们只是一种类型。

但由于某种原因,它认为“第三元素”只有一种类型

In [5]: my_type=Type.objects.get(name='My Type')

In [6]: my_type.element_set.annotate(num_types=Count('type')).filter(num_types__exact=1)
Out[6]: [<Element: First Element>, <Element: Third Element>]

In [7]: [(e,e.num_types) for e in my_type.element_set.annotate(num_types=Count('type'))]
Out[7]: [(<Element: First Element>, 1), (<Element: Third Element>, 1)]

当它有三种类型时

In [8]: Element.objects.get(name='Third Element').type.count()
Out[8]: 3

我做错了什么?

3 个答案:

答案 0 :(得分:0)

鉴于这是标准的多对多,您可以调用'my_type.element_set.all()',它应该为您提供该类型的Element实例。您也可以采用其他方式(使用模型名称的复数版本,而不是'_set')来获取类似的元素类型:'my_element.types.all()'。

要获得计数,只需执行'my_type.element_set.count()'。

答案 1 :(得分:0)

Django在幕后执行的SQL查询将您限制为仅包含type_element_type.element_id = 1的行。因此,带注释的计数仅适用于那些,因此每个都有1个。

上面的查询[7]变成了这个SQL(如this所示):

SELECT "type_element"."id", "type_element"."name", COUNT("type_element_type"."type_id") 
AS "num_types" FROM "type_element" LEFT OUTER JOIN "type_element_type"
ON ("type_element"."id" = "type_element_type"."element_id")
WHERE "type_element_type"."type_id" = 1 
GROUP BY "type_element"."id", "type_element"."name"

和sqlite3:

sqlite> SELECT * FROM "type_element" LEFT OUTER JOIN "type_element_type" ON ("type_element"."id" = "type_element_type"."element_id") WHERE "type_element_type"."type_id" = 1;
1|First Element|1|1|1
3|Third Element|3|3|1

答案 2 :(得分:0)

我结束了

Element.objects.filter(pk__in=my_type.element_set.values_list('pk', flat=True)).annotate(num_types=Count('type')).filter(num_types=1)

这样,当我从my_type.element_set

开始时,我不会受到连接的影响

如果您想知道所涉及的SQL:

my_type.element_set.annotate(num_types=Count('type')).filter(num_types__exact=1)它确实:

SELECT `senalesweb_element`.`id`, `senalesweb_element`.`name`, COUNT(`senalesweb_element_type`.`type_id`) AS `num_types` FROM `senalesweb_element` LEFT OUTER JOIN `senalesweb_element_type` ON (`senalesweb_element`.`id` = `senalesweb_element_type`.`element_id`) WHERE (`senalesweb_element_type`.`type_id` = 1 ) GROUP BY `senalesweb_element`.`id` HAVING COUNT(`senalesweb_element_type`.`type_id`) = 1  ORDER BY NULL LIMIT 21

Element.objects.filter(pk__in=my_type.element_set.values_list('pk', flat=True)).annotate(num_types=Count('type')).filter(num_types=1)中,它确实:

SELECT `senalesweb_element`.`id`, `senalesweb_element`.`name`, COUNT(`senalesweb_element_type`.`type_id`) AS `num_types` FROM `senalesweb_element` LEFT OUTER JOIN `senalesweb_element_type` ON (`senalesweb_element`.`id` = `senalesweb_element_type`.`element_id`) WHERE (`senalesweb_element`.`id` IN (SELECT U0.`id` FROM `senalesweb_element` U0 INNER JOIN `senalesweb_element_type` U1 ON (U0.`id` = U1.`element_id`) WHERE U1.`type_id` = 1 )) GROUP BY `senalesweb_element`.`id` HAVING COUNT(`senalesweb_element_type`.`type_id`) = 1  ORDER BY NULL LIMIT 21