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进行分组?
或者,最好是请求部门,然后以某种方式用产品计数注释它们?
答案 0 :(得分:37)
尽可能避免使用extra
和raw
。 aggregation docs几乎有这个用例:
直接来自文档:
# 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
因此,要针对您的特定示例进行修改:
depts = Department.objects.
filter(product__review__time__range=["2012-01-01", "2012-01-08"]).
annotate(num_products=Count('product'))
单独行上的函数调用只是为了便于阅读,你应该相应地移动它们。我没有对此进行测试,但我认为它应该可行。
答案 1 :(得分:6)
我在过去几天内不得不做几个类似的查询,并且最简单的方法是使用extra
查询集函数来注释查询集中的每个对象,并使用过滤的产品计数: / p>
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])
和
{% for department in departments %}
{{ department.product_count }}
{% endfor %}
答案 2 :(得分:2)
在Django >= 2.0
中,这可以通过
depts = Department.objects.all().annotate(
num_products=Count('product', filter=Q(product__review__time__range=["2012-01-01", "2012-01-08"]))
)
有关更多信息,请阅读文档https://docs.djangoproject.com/en/2.0/topics/db/aggregation/#filtering-on-annotations
答案 3 :(得分:0)
汇总的文件 https://docs.djangoproject.com/en/dev/topics/db/aggregation/#cheat-sheet
可能有一种方法可以使用聚合或注释,但我更喜欢这个:
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()
我认为如果你要做一个原始查询(类似这样的话)
sql = """SELECT COUNT(Product.*) as num_products, Department.*
FROM Department
LEFT OUTER JOIN Product ON Product.department = Department.id
LEFT OUTER JOIN Review ON Product.id = Review.product
WHERE Review.time BETWEEN "2012-01-01" AND "2012-01-08"
GROUP BY Department.id"""
Department.objects.raw(sql)
然后num_products将是结果中每个Dept实例的属性。
您可能需要稍微使用字段+表名称
答案 4 :(得分:0)
我对类似的数据模型有相同的情况。
我的解决方案就像:
Department.objects \
.extra(where=["<review_table_name.time_field> BETWEEN <time1> AND <time2> "])\
.annotate(num_products=Count('product__review__product_id'))