【问题标题】:Django aggregation and queriesDjango 聚合和查询
【发布时间】:2010-07-01 20:49:58
【问题描述】:

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

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

我做错了什么?

【问题讨论】:

    标签: django django-queryset


    【解决方案1】:

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

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

    【讨论】:

      【解决方案2】:

      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
      

      【讨论】:

      • 我猜测原因是连接,但我在询问一种重写查询的方法。最后我找到了解决办法。
      【解决方案3】:

      我做完了

      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
      

      【讨论】:

        猜你喜欢
        • 2012-04-01
        • 1970-01-01
        • 2011-09-01
        • 1970-01-01
        • 2023-03-29
        • 1970-01-01
        • 2018-12-04
        • 1970-01-01
        • 2017-07-21
        相关资源
        最近更新 更多