Django 聚合和查询

发布于 2024-09-08 02:10:47 字数 1378 浏览 2 评论 0原文

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

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

我正在尝试获取一个查询集,其中包含仅属于“我的类型”类型的元素

我的想法是获取此类型的元素并检查它们是否仅属于一种类型。

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

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

我做错了什么?

I have a number of elements that can be of one or more types.

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

Let's say that I have 3 types and 3 elements:

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

I'm trying to get a queryset with the elements that are only of the type "My Type"

My idea was to get the elements of this type and check that they are only of one type.

But for some reason it thinks that "Third Element" is of only one 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)]

when it is of three types

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

What I'm doing wrong?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

若相惜即相离 2024-09-15 02:10:47

鉴于这是标准的多对多,您可以调用“my_type.element_set.all()”,它应该为您提供该类型的 Element 实例。您还可以采用另一种方式(使用模型名称的复数版本,而不是“_set”)来获取元素类型,如下所示:“my_element.types.all()”。

要获取计数,只需执行“my_type.element_set.count()”即可。

Given that this is a standard many-to-many, you can call 'my_type.element_set.all()', that should give you the Element instances that are of that type. You can also go the other way (using pluralized version of model name, not '_set') to get an elements types like so: 'my_element.types.all()'.

To get the count, just do 'my_type.element_set.count()'.

許願樹丅啲祈禱 2024-09-15 02:10:47

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

上面的查询 [7] 变为此 SQL(如 这个):

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

The SQL query that Django is performing behind the scenes is limiting you to only the rows that contain type_element_type.element_id = 1. So the annotated count is only applying to those, hence 1 each.

Your query [7] above becomes this SQL (as shown by 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"

and from 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
ㄖ落Θ余辉 2024-09-15 02:10:47

我结束了

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

I ended doing

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

That way I'm not affected by the join that happens when I start with my_type.element_set

If you are wondering about the SQL involved:

In my_type.element_set.annotate(num_types=Count('type')).filter(num_types__exact=1) it does:

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

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

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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文