Python Django:如何使用过滤的ForeignKey字段的计数来注释queryset?

Python Django:如何使用过滤的ForeignKey字段的计数来注释queryset?,python,django,django-models,Python,Django,Django Models,Django新手问题:) 我有以下模型-每个评审针对一个产品,每个产品都有一个部门: class Department(models.Model): code = models.CharField(max_length=16) class Product(models.Model): id = models.CharField(max_length=40, primary_key=True, db_index=True) dept = models.ForeignKey(

Django新手问题:)

我有以下模型-每个评审针对一个产品,每个产品都有一个部门:

class Department(models.Model):
    code = models.CharField(max_length=16)
class Product(models.Model):
    id = models.CharField(max_length=40, primary_key=True, db_index=True)
    dept = models.ForeignKey(Department, null=True, blank=True, db_index=True)
class Review(models.Model):
    review_id = models.CharField(max_length=32, primary_key=True, db_index=True) 
    product = models.ForeignKey(Product, db_index=True) 
    time = models.DateTimeField(db_index=True) 
我想对日期范围(2012-01-01到2012-01-08)进行Django查询,并返回所有部门的列表,用部门ID注释,以及在该日期范围内审查的来自该部门的产品数量

这有点让我头疼:)

我可以在一个时间范围内获得所有评论:

 reviews = Review.filter(time__range=["2012-01-01", "2012-01-08"])
然后我猜每个评论都有一个产品字段,每个产品都有一个部门代码。但我如何根据产品和代码、计数和部门ID对它们进行分组

或者,是否最好请求部门,然后以某种方式用产品数量对其进行注释

用于聚合的文档

可能有一种方法可以使用聚合或注释,但我更喜欢这样:

departments = Department.objects.all()
for dept in departments : 
    # Get the number of reviewed products for a given range and department
    num_products = dept.product_set.filter(review__time__range=["2012-01-01", "2012-01-08"]).count()
如果您绝对需要它作为模型的一个功能:

class Department(models.Model) :
    ...
    def num_products(self, start_date, end_date) : 
        return self.product_set.filter(review__time__range=[start_date, end_date]).count()
编辑 我想如果你要做一个原始的查询(类似这样的事情)

然后num_products将成为结果中每个Dept实例的一个属性


您可能需要稍微处理一下字段+表名

在过去几天中,我不得不进行了几次类似的查询,最简单的方法是使用queryset函数对queryset中的每个对象进行注释,并对产品进行过滤计数:

start = ..  # need to be formatted correctly
end = ...
departments = Departments.objects.all().extra(select = {
     'product_count' : """ SELECT COUNT(*) FROM appname_department
                           JOIN appname_product
                               ON appname_product.dept_id = appname_department.id
                           JOIN appname_review 
                               ON appname_review.product_id = appname_product.id
                           WHERE appname_review.time BETWEEN %s AND %s
                       """
}, params=[start, end])


尽可能避免
extra
raw
。用户几乎可以使用此用例:

直接从文档中:

# Each publisher, each with a count of books as a "num_books" attribute.
>>> from django.db.models import Count
>>> pubs = Publisher.objects.annotate(num_books=Count('book'))
>>> pubs
[<Publisher BaloneyPress>, <Publisher SalamiPress>, ...]
>>> pubs[0].num_books
73

单独行上的函数调用只是为了可读性,您应该相应地移动它们。我还没有对此进行测试,但我认为它应该可以工作。

我对类似的数据模型也有同样的情况

我的解决方案是:

 Department.objects \
.extra(where=["<review_table_name.time_field> BETWEEN <time1> AND <time2> "])\
.annotate(num_products=Count('product__review__product_id'))
Department.objects\
.extra(其中=[“介于和”])\
.annotate(num\u products=Count('product\u review\u product\u id'))

在django
=2.0中,这可以通过

depts = Department.objects.all().annotate(
    num_products=Count('product', filter=Q(product__review__time__range=["2012-01-01", "2012-01-08"]))
)

有关更多信息,请阅读文档。每个部门一个查询。好的,我添加了一个只执行一个查询的解决方案。您不接受我的答案-我可以假设它工作不正常吗?如果是这样的话,为什么它是错误的,你是否以另一种方式使它工作,并且你能发布你的解决方案作为一个答案来帮助其他人在未来?非常肯定这将是一个错误。无法将关键字“product”解析为字段。选择包括:code@KrisF你可能是对的-我在寻找反向关系计数。。我必须用实际的代码进行实验,才能让它正常工作。我正试图解决这样一个问题-我很确定原始查询是必要的。等等,我现在很困惑。在我的场景中,我有CourseProject-->CourseUnit-->课程,这类似于OP的Review-->Product-->部门。我可以执行以下查询以获得课程列表,并根据课程项目所包含的课程项目(类似于OP的目标)用满足某些条件的课程单元数进行注释:Course.objects.filter(courseunit\uuuuuuuuuCourseProject\uuuuuuuuuuu number\uuuuu gt=1)。注释(num\u units=Count('CourseUnits'),即“获取所有课程,并用其包含的单元数对其进行注释,这些单元的项目数大于1“.Django 1。5@dshap现在看来这是可能的。我不确定在1月13日是否可能。Django 1.5尚未发布。也许这在当时是可能的。我可以发誓我是在试图解决这个问题。也许我忽略了它,或者它是新的。不管是哪种方式,都要投赞成票。此答案现在可能应该标记为已接受。此示例易受SQL注入攻击
depts = Department.objects.
            filter(product__review__time__range=["2012-01-01", "2012-01-08"]).
            annotate(num_products=Count('product'))
 Department.objects \
.extra(where=["<review_table_name.time_field> BETWEEN <time1> AND <time2> "])\
.annotate(num_products=Count('product__review__product_id'))
depts = Department.objects.all().annotate(
    num_products=Count('product', filter=Q(product__review__time__range=["2012-01-01", "2012-01-08"]))
)