[Django]-Django get all values Group By particular one field

4๐Ÿ‘

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`
๐Ÿ‘คKonstantin F.

1๐Ÿ‘

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.

๐Ÿ‘คPeter Galfi

1๐Ÿ‘

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.

0๐Ÿ‘

query = MenuPermission.objects.values('menu_id').annotate(menu_id_count=Count('menu_id'))

You can check your SQL query by print(query.query)

๐Ÿ‘คbysucpmeti

0๐Ÿ‘

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")
            )
๐Ÿ‘คBrian H.

-3๐Ÿ‘

you can use the following code:

 MenuPermission.objects.values('menu_id').annotate(Count('id)).values('field1', 'field2', 'field3'...)
๐Ÿ‘คmango

Leave a comment