The annotation 'id' conflicts with a field on the model - django

In Annotate I am trying to get the count of quires for which is_healthy is True but I am getting an Error The annotation 'id' conflicts with a field on the model.
Any solution to solve this? and why is this causing how can i resolve this?
DeviceHealthHistory.objects.filter(**filter_data).values(
id = F('id'),
).annotate(
healthy_count=Count('id', filter=Q(is_healthy=True)),
)

If you are just looking for count then you use count function fo queryset:
DeviceHealthHistory.objects.filter(**filter_data).filter(is_healthy=True).count()
To get other fields along with the count.
DeviceHealthHistory.objects.filter(**filter_data).values(
'other_field1'
).annotate(
healthy_count=Count('id', filter=Q(is_healthy=True)),
)

You should count with:
DeviceHealthHistory.objects.filter(**filter_data, is_healthy=True).count()
This will filter on the **filter_data and also ensure that it only counts records with is_healthy=True. We then count the number of records.
If you want to "group by" a certain field, like patient_id, you can work with:
DeviceHealthHistory.objects.filter(**filter_data).values('patient_id').annotate(
n=Count('pk', filter=Q(is_healthy=True))
).order_by('patient_id')
This will produce a queryset of dictionaries with 'patient_id' and 'n' as keys, and the patient and the corresponding counts as values.

Related

Django group by multiple column and get count of unique combination

In my model there are 10 fields, out of which below four I need to group on,
department
city
state
zip
And then get count of records which has same combination of these values
Example
IT|Portland|Oregon|11111 => 100
I tried annotate however it is not giving me desired results. Please advice
from django.db.models import Count
YourModel.objects.values('department', 'city', 'state', 'zip').annotate(count=Count('id'))

How to count number of filtered columns in django with fk?

In the bellow code the user_name is foreign key. I want to get the name and I am getting fk id.
test.objects.filter(status='present').order_by('user_name').values('user_name').annotate(dcount=Count('status'))
As far as I can understand, you want a count of all the test objects with status='present'. Try:
test.objects.filter(status='present').count()
this will get the count of all attendance with status present. context['attendance_obj']=Attendance.objects.filter(status='present').order_by('user_name').values('user_name').annotate(dcount=Count('status'))

using Filtered Count in django over joined tables returns wrong values

To keep it simple I have four tables(A, B, Category and Relation), Relation table stores the Intensity of A in B and Category stores the type of B.
A <--- Relation ---> B ---> Category
(So the relation between A and B is n to n, where the relation between B and Category is n to 1)
What I need is to calculate the occurrence rate of A in Category which is obtained using:
A.objects.values(
'id', 'relation_set__B__Category_id'
).annotate(
ANum = Count('id', distinct=False)
)
Please notice that If I use 'distinct=True' instead every and each 'Anum' would be equal to 1 which is not the desired outcome. The problem is that I have to filter the calculation based on the dates that B has been occurred on(and some other fields in B table),
I am using django 2.0's feature which makes using filter as an argument in aggregation possible.
Let's assume:
kwargs= {}
kwargs['relation_set__B____BDate__gte'] = the_start_limit
I could use it in my code like:
A.objects.values(
'id', 'relation_set__B__Category_id'
).annotate(
Anum = Count('id', distinct=False, filter=Q(**kwargs))
)
However the result I get is duplicated due to the table joins and I cannot use distinct=True as I explained. (querying A is also a must since I have to aggregate some other fields on this table as explained in my question here)
I am using Postgres and django 2.0.1 .
Is there any workarounds to achieve what I have in mind?
Update
Got it done using another Subquery:
# subquery
annotation = {
'ANum': Count('relation_set__A_id', distinct=False,
filter=Q(**Bkwargs),
}
sub_filter = Q(relation_set__A_id=OuterRef('id')) &
Q(Category_id=OuterRef('relation_set__B__Category_id'))
# you could annotate 'relation_set__B__Category_id' to A query an set the field here.
subquery = B.objects.filter(
sub_filter
).values(
'relation_set__A_id'
).annotate(**annotation).values('ANum')[:1]
# main query
A.objects.values(
'id', 'relation_set__B__Category_id'
).annotate(
Anum = Subquery(subquery)
)
I'm still not sure if I understood what you want. You write
Please notice that If I use 'distinct=True' instead every and each 'Anum' would be equal to 1
Of course. You count the associated A-object to each A-object. Each counts itself. So I still think you don't want to annotate A-objects with Anum, but probably Categories. This one should give you the desired number of As in each Category.
Category.objects.annotate(
Anum=Count(
'b__relation__a',
filter=Q(b__BDate__gte=the_start_limit),
distinct=True
)
)
'b__relation__a' follows the relations backwards and picks all A-objects that are related to the Category. However the filter limits the counted relations to certain Bs. The distinct=True is needed to avoid a query bug.
If you really want "a list of A objects grouped by its id" (and not only the aggregated Anum-count), as you stated in your comment, I don't see an easy way to do that in a single query.

Django get all values Group By particular one field

I want to execute a simple query like:
select *,count('id') from menu_permission group by menu_id
In Django format I have tried:
MenuPermission.objects.all().values('menu_id').annotate(Count('id))
It selects only menu_id. The executed query is:
SELECT `menu_permission`.`menu_id`, COUNT(`menu_permission`.`id`) AS `id__count` FROM `menu_permission` GROUP BY `menu_permission`.`menu_id`
But I need other fields also. If I try:
MenuPermission.objects.all().values('id','menu_id').annotate(Count('id))
It adds 'id' in group by condition.
GROUP BY `menu_permission`.`id`
As a result I am not getting the expected result. How I can get all all fields in the output but group by a single one?
You can try subqueries to do what you need.
In my case I have two tables: Item and Transaction where item_id links to Item
First, I prepare Transaction subquery with group by item_id where I sum all amount fields and mark item_id as pk for outer query.
per_item_total=Transaction.objects.values('item_id').annotate(total=Sum('amount')).filter(item_id=OuterRef('pk'))
Then I select all rows from item plus subquery result as total filed.
items_with_total=Item.objects.annotate(total=Subquery(per_item_total.values('total')))
This produces the following SQL:
SELECT `item`.`id`, {all other item fields},
(SELECT SUM(U0.`amount`) AS `total` FROM `transaction` U0
WHERE U0.`item_id` = `item`.`id` GROUP BY U0.`item_id` ORDER BY NULL) AS `total` FROM `item`
You are trying to achieve this SQL:
select *, count('id') from menu_permission group by menu_id
But normally SQL requires that when a group by clause is used you only include those column names in the select that you are grouping by. This is not a django matter, but that's how SQL group by works.
The rows are grouped by those columns so those columns can be included in select and other columns can be aggregated if you want them to into a value. You can't include other columns directly as they may have more than one value (since the rows are grouped).
For example if you have a column called "permission_code", you could ask for an array of the values in the "permission_code" column when the rows are grouped by menu_id.
Depending on the SQL flavor you are using, this could be in PostgreSQL something like this:
select menu_id, array_agg(permission_code), count(id) from menu_permissions group by menu_id
Similary django queryset can be constructed for this.
Hopefully this helps, but if needed please share more about what you need to do and what your data models are.
The only way currently that it works as expected is to hve your query based on the model you want the GROUP BY to be based on.
In your case it looks like you have a Menu model (menu_id field foreign key) so doing this would give you what you want and will allow getting other aggregate information from your MenuPermission model but will only group by the Menu.id field:
Menu.objects.annotate(perm_count=Count('menupermission__id')).values('perm_count')
Of course there is no need for the "annotate" intermediate step if all you want is that single count.
query = MenuPermission.objects.values('menu_id').annotate(menu_id_count=Count('menu_id'))
You can check your SQL query by print(query.query)
This solution doesn't work, all fields end up in the group by clause, leaving it here because it may still be useful to someone.
model_fields = queryset.model._meta.get_fields()
queryset = queryset.values('menu_id') \
.annotate(
count=Count('id'),
**{field.name: F(field.name) for field in model_fields}
)
What i'm doing is getting the list of fields of our model, and set up a dictionary with the field name as key and an F instance with the field name as a parameter.
When unpacked (the **) it gets interpreted as named arguments passed into the annotate function.
For example, if we had a "name" field on our model, this annotate call would end up being equal to this:
queryset = queryset.values('menu_id') \
.annotate(
count=Count('id'),
name=F("name")
)
you can use the following code:
MenuPermission.objects.values('menu_id').annotate(Count('id)).values('field1', 'field2', 'field3'...)

Filtering QuerySet by __count of RelatedManager

I've got a QuerySet I'd like to filter by the count of a related_name. Currently I've got something like this:
objResults = myObjects.filter(Q(links_by_source__status=ACCEPTED),Q(links_by_source__count=1))
However, when I run this I get the following error message:
Cannot resolve keyword 'count' into field
I'm guessing that this query is operating individually on each of the links_by_source connections, therefore there is no count function since it's not a QuerySet I'm working with. Is there a way of filtering so that, for each object returned, the number of links_by_source is exactly 1?
You need to use an aggregation function to get the count before you can filter on it.
from django.db.models import Count
myObjects.filter(
links_by_source__status=ACCEPTED).annotate(link_count=Count('links_by_source')
).filter(link_count=1)
Note, you should pay attention to the order of the annotate and filter here: that query counts the number of ACCEPTED links, not sure if you want that or you want to check that the total count of all links is 1.