首页 > 解决方案 > Django 上 Count 函数的不同行为

问题描述


我有一个非常奇怪的情况,如果将 Count() 应用于通过 id 或其他过滤器过滤的模型,它的行为会有所不同。我有这些模型:
class Segment(models.Model):
    is_completed = models.BooleanField(default=False)

class Waypoint(models.Model):
    is_visited = models.BooleanField("is visited", default=False)
    segment = models.ForeignKey("Segment", on_delete=models.PROTECT, related_name="waypoints", null=True, default=None)

假设我们有两个尚未访问的航路点,都与同一航段相关。我在查询集中有这些航点之一,称之为wp。现在,如果我执行:

to_visit_filter = Q(waypoints__is_visited=False)
seg = Segment.objects.filter(waypoints__in=wp, is_completed=False).annotate(
            wp_to_visit=Count('waypoints', filter=to_visit_filter))

print(seg.first().wp_to_visit)   // 1

相反,如果我这样做:

to_visit_filter = Q(waypoints__is_visited=False)
segm_id = [w.segment.id for w in wp]
seg = Segment.objects.filter(id__in=segm_id, is_completed=False).annotate(
            wp_to_visit=Count('waypoints', filter=to_visit_filter))

print(seg.first().wp_to_visit)   // 2

在这两种情况下都seg.first()给出相同的对象实例,但要访问的路点数不同。为什么?

标签: djangodjango-modelsdjango-querysetdjango-filter

解决方案


In your first query you perform filtering on the waypoints (waypoints__in=wp) and hence have limited the waypoints to that one selected Waypoint. This filtering results in a LEFT OUTER JOIN with the Waypoint model and a WHERE clause. Next you perform aggregation and group by on waypoints by using Count on waypoints. This annotation would use the same join made by the previous call to filter and hence obviously your count has the result of 1.

Next in your second query instead of filtering on waypoints, you filter on the segments id itself, hence there is no filtering on the waypoints and your query gives you the count of 2.


推荐阅读