【问题标题】:Django filtering based on count of related modelDjango基于相关模型的计数过滤
【发布时间】:2018-03-01 08:55:11
【问题描述】:

我有以下工作代码:

houses_of_agency = House.objects.filter(agency_id=90)
area_list = AreaHouse.objects.filter(house__in=houses_of_agency).values('area')
area_ids = Area.objects.filter(area_id__in=area_list).values_list('area_id', flat=True)

这将返回一个带有 area_id 列表的查询集。我想进一步过滤,以便仅获得属于该机构的超过 100 间房屋的 area_ids。

我尝试了以下调整:

houses_of_agency = House.objects.filter(agency_id=90)
area_list = AreaHouse.objects.filter(house__in=houses_of_agency).annotate(num_houses=Count('house_id')).filter(num_houses__gte=100).values('area')
area_ids = Area.objects.filter(area_id__in=area_list).values_list('area_id', flat=True)

但它返回一个空查询集。

我的模型(简化)如下所示:

class House(TimeStampedModel):
    house_pk = models.IntegerField()
    agency = models.ForeignKey(Agency, on_delete=models.CASCADE)


class AreaHouse(TimeStampedModel):
    area = models.ForeignKey(Area, on_delete=models.CASCADE)
    house = models.ForeignKey(House, on_delete=models.CASCADE)


class Area(TimeStampedModel):
    area_id = models.IntegerField(primary_key=True)
    parent = models.ForeignKey('self', null=True)
    name = models.CharField(null=True, max_length=30)

编辑:我使用 MySQL 作为数据库后端。

【问题讨论】:

    标签: python django python-3.x django-models


    【解决方案1】:

    您查询agency_id 时只使用了一个下划线。我在下面更正了您的查询。此外,在 django 中,使用 pk 而不是 id 更为常见,但行为是相同的。此外,不需要三个单独的查询,因为您可以将所有内容合并为一个。

    还请注意,您的字段area_idhouse_pk 是不必要的,django 会自动创建可通过modelname__pk 访问的主键字段。

    # note how i inlined your first query in the .filter() call
    area_list = AreaHouse.objects \
                .filter(house__agency__pk=90) \
                .annotate(num_houses=Count('house')) \  # <- 'house'
                .filter(num_houses__gte=100) \
                .values('area')
    
    # note the double underscore
    area_ids = Area.objects.filter(area__in=area_list)\
                           .values_list('area__pk', flat=True)
    

    如果您不需要中间结果,您可以进一步简化此操作。这是两个查询的组合:

    area_ids = AreaHouse.objects \
                .filter(house__agency__pk=90) \
                .annotate(num_houses=Count('house')) \
                .filter(num_houses__gte=100) \
                .values_list('area__pk', flat=True)
    

    最后,您似乎在模型中手动定义了多对多关系(通过AreaHouse)。有更好的方法可以做到这一点,请阅读django docs

    【讨论】:

    • 实际上它似乎确实有效。 num_houses 始终为 1,因此没有给出该区域的实际房屋数量。
    • 那是一个错字。它不起作用。 num_houses 等于 1,并且没有给出该区域的实际房屋数量
    • 您可能需要检查您的数据。查询看起来正确。
    • 我对您的回答进行了编辑。它对我有用。它认为这可能只需要 MySQL 作为数据库后端。我在我的问题中指定我正在使用 MySQL。一旦我的编辑被接受,我会接受你的回答。感谢您的帮助。
    • 我无法接受这些修改。它们不是 django 最佳实践,并且会使其他发现此问题的人感到困惑。我建议您在 cmets 中发布您的修复 - 其他人应该遵循 django 最佳实践并使用适当的 ORM 字段来实现多对多关系。
    猜你喜欢
    • 2020-08-15
    • 1970-01-01
    • 2020-10-30
    • 1970-01-01
    • 2017-06-22
    • 1970-01-01
    • 2020-01-19
    • 2020-12-21
    • 2021-04-10
    相关资源
    最近更新 更多