46👍
Avoid extra
and raw
whenever possible. The aggregation docs have nearly this use case:
Straight from the 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
So, to modify this for your particular example:
depts = Department.objects.
filter(product__review__time__range=["2012-01-01", "2012-01-08"]).
annotate(num_products=Count('product'))
The function calls on separate lines is just for readability and you should move them about accordingly. I haven’t tested this, but I think it should work.
11👍
In django >= 2.0
this could be achieved with
depts = Department.objects.all().annotate(
num_products=Count('product', filter=Q(product__review__time__range=["2012-01-01", "2012-01-08"]))
)
For more info read docs https://docs.djangoproject.com/en/2.0/topics/db/aggregation/#filtering-on-annotations
- [Django]-Gunicorn Connection in Use: ('0.0.0.0', 5000)
- [Django]-Python/Django: log to console under runserver, log to file under Apache
- [Django]-Django REST Framework: how to substitute null with empty string?
5👍
I’ve had to do a couple of similar queries in the last few days and the easiest way it to use the extra
queryset function to annotate each object in your queryset with a filtered count of the products:
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])
and
{% for department in departments %}
{{ department.product_count }}
{% endfor %}
- [Django]-Django ModelChoiceField optgroup tag
- [Django]-In the Django admin interface, is there a way to duplicate an item?
- [Django]-Rendering a template variable as HTML
0👍
The Docs for aggregation
https://docs.djangoproject.com/en/dev/topics/db/aggregation/#cheat-sheet
There probably is a way to use aggregate or annotate, but I prefer this:
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()
if you absolutely need it as a function of the model:
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()
EDIT
I think that if you were to do a raw query (something like this)
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)
and then num_products will be an attribute on every Dept instance in the results.
you may need to play with the field + table names a little
- [Django]-Parsing a Datetime String into a Django DateTimeField
- [Django]-Django debug display all variables of a page
- [Django]-How do I do a not equal in Django queryset filtering?
0👍
I have the same situation with similar data model.
My solution was like:
Department.objects \
.extra(where=["<review_table_name.time_field> BETWEEN <time1> AND <time2> "])\
.annotate(num_products=Count('product__review__product_id'))
- [Django]-How do I get the values of all selected checkboxes in a Django request.POST?
- [Django]-How to express a One-To-Many relationship in Django?
- [Django]-List field in model?