【问题标题】:Django: Annotation not working?Django:注释不起作用?
【发布时间】:2012-09-19 16:34:41
【问题描述】:

我有两个模型,Product,它与 RatingEntry 是一对多的关系:

>>> product_entries = models.Products.objects.all()
>>> annotated = product_entries.annotate(Count("ratingentry"))
>>> len(annotated)
210
>>> a = annotated.filter(ratingentry__count__lte = 10)
>>> b = annotated.filter(ratingentry__count__gt = 10)
>>> len(a)
10
>>> len(b)
200
>>> len(a | b)
10 //should be 210

如果我将 a 和 b 更改为列表并将它们连接起来,则长度为 210。

知道这里发生了什么吗?

【问题讨论】:

    标签: python django annotations


    【解决方案1】:

    查询集不支持按位包含。 Django 没有引发错误,而是将其视为逻辑 OR 并返回第一个计算 True 的值。因为它们都是有效的查询集,所以总是返回第一个。

    如果您想实际组合两个查询集,您要么需要将它们转换为列表,然后将一个与另一个扩展,要么使用类似 itertools.chain 的东西,但最终会得到一个无法实现的生成器用于除迭代之外的任何事情。无论哪种方式,组合查询集都将禁止对这些查询集进行任何进一步的操作。

    【讨论】:

    • 我认为这不对,我过去使用此运算符组合了 QuerySet,并且成功了。举个例子:>>> a = models.Product.objects.filter(kind = "clothes") >>> b = models.Product.objects.filter(kind = "technology") >>> len(a) 90 >>> len(b) 113 >>> len(a | b) 203 >>>
    • 你可以做a = Q(ratingentry__count__lte=10);b = Q(ratingentry__count__gt=10);annotated.filter(a|b)
    【解决方案2】:

    我认为这种行为是 Django 对象关系映射中的一个错误。如果您查看 Django 为您的查询生成的 SQL,您会看到如下内容:

    >>> q1 = (Products.objects.annotate(num_ratings = Count('ratingentries'))
    ...       .filter(num_ratings__gt = 10))
    >>> q2 = (Products.objects.annotate(num_ratings = Count('ratingentries'))
    ...       .exclude(num_ratings__gt = 10))
    >>> print(str((q1 | q2).query))
    SELECT `myapp_products`.`id`, COUNT(`myapp_ratingentries`.`id`) AS
    `num_ratings` FROM `myapp_products` LEFT OUTER JOIN `myapp_ratingentries` ON
    (`myapp_products`.`id` = `myapp_ratingentries`.`product_id`) GROUP BY
    `myapp_products`.`id` HAVING COUNT(`myapp_ratingentries`.`id`) > 10
    ORDER BY NULL
    

    请注意,来自q1 的条件包含在查询的HAVING 子句中,但来自q2 的条件已丢失。

    您可以通过如下方式构建查询来解决此问题:

    >>> q = Q(num_products__gt = 10) | ~Q(num_products__gt = 10)
    >>> q3 = Products.objects.annotate(num_ratings = Count('ratingentries')).filter(q)
    >>> print(str(q3.query))
    SELECT `myapp_products`.`id`, COUNT(`myapp_ratingentries`.`id`) AS
    `num_ratings` FROM `myapp_products` LEFT OUTER JOIN `myapp_ratingentries` ON
    (`myapp_products`.`id` = `myapp_ratingentries`.`product_id`) GROUP BY
    `myapp_products`.`id` HAVING (COUNT(`myapp_ratingentries`.`id`) > 10 OR NOT
    (COUNT(`myapp_ratingentries`.`id`) > 10 )) ORDER BY NULL
    

    请注意,这两个条件现在都包含在 HAVING 子句中。

    我建议你report this to the Django developers as a bug。 (如果无法修复,那么至少应该记录在案。)

    【讨论】:

    • 这个解释有道理,我会尽量确保它不是我的一些愚蠢的错误,并报告这个。
    猜你喜欢
    • 2018-06-01
    • 1970-01-01
    • 2014-08-14
    • 1970-01-01
    • 2013-09-20
    • 2020-10-09
    • 2015-09-14
    • 2017-08-20
    • 1970-01-01
    相关资源
    最近更新 更多