How can I select multiple columns from a Django model? - django

At the moment I have a query that selects distinct values from a model:
Meeting.objects.values('club').distinct()
In addition to the 'club' field, I also wish to select a 'time' field. In other words I wish to select distinct values of the 'club' field and the associated 'time' field. For example for:
CLUB,TIME
ABC1,10:35
ABC2,10:45
ABC2,10:51
ABC3,11:42
I would want:
ABC1,10:35
ABC2,10:45
ABC3,11:42
What is the syntax for this?

This is possible, but only if your database backend is PostgreSQL. Here how it can be done:
Meeting.objects.order_by('club', 'time').values('club', 'time').distinct('club')
Look documentation for distinct

Related

django Get Distinct rows which are inserted last

I have a table which same as bellow
id|user|datetime|action
this table records actions for each user and its time I want to get latest actions which are done by all users. I have tested these methods
Entry.objects.order_by('datetime','user').distinct('user')
and
Entry.objects.latest('datetime').distinct('user')
but both of them returns errors. for example the second try says :
SELECT DISTINCT ON expressions must match initial ORDER BY expressions
How can I filter rows by latest actions which are done by all users?
Try with below code:
Entry.objects.all().order_by('user', '-datetime').distinct('user')
From django doc we have :
When you specify field names, you must provide an order_by() in the QuerySet, and the fields in order_by() must start with the fields in distinct(), in the same order.

Django - joining multiple tables (models) and filtering out based on their attribute

I'm new to django and ORM in general, and so have trouble coming up with query which would join multiple tables.
I have 4 Models that need joining - Category, SubCategory, Product and Packaging, example values would be:
Category: 'male'
SubCategory: 'shoes'
Product: 'nikeXYZ'
Packaging: 'size_36: 1'
Each of the Model have FK to the model above (ie. SubCategory has field category etc).
My question is - how can I filter Product given a Category (e.g. male) and only show products which have Packaging attribute available set to True? Obviously I want to minimise the hits on my database (ideally do it with 1 SQL query).
I could do something along these lines:
available = Product.objects.filter(packaging__available=True)
subcategories = SubCategory.objects.filter(category_id=<id_of_male>)
products = available.filter(subcategory_id__in=subcategories)
but then that requires 2 hits on database at least (available, subcategories) I think. Is there a way to do it in one go?
try this:
lookup = {'packaging_available': True, 'subcategory__category_id__in': ['ids of males']}
product_objs = Product.objects.filter(**lookup)
Try to read:
this
You can query with _set, multi __ (to link models by FK) or create list ids
I think this should work but it's not tested:
Product.objects.filter(packaging__available=True,subcategori‌​es__category_id__in=‌​[id_of_male])
it isn't tested but I think that subcategories should be plural (related_name), if you didn't set related_name, then subcategory__set instead od subcategories should work.
Probably subcategori‌​es__category_id__in=‌​[id_of_male] can be switched to .._id=id_of_male.

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'...)

Django Model Distinct clause

I have a django model query that needs to fetch me the distinct rows from a particular table, but for some reason its not fetching it.
Let me know your inputs
Query
Volunteercontacts.objects.order_by('name')- gives me the expected answer ie;ordering by name
Volunteercontacts.objects.order_by('name').distinct('name')- does not eliminate the duplicate
You should differentiate between distinct Volunteercontacts models and distinct name column values.
For distinct Volunteercontacts models you can use distinct(), but in this case has no effect:
Volunteercontacts.objects.order_by('name').distinct()
For distinct columns values you can use a dictionary or array of value list:
Volunteercontacts.objects.values_list('name',
flat=True).order_by('name').distinct()
Also, remember that the ability to specify field names in distinct method is only available in PostgreSQL.
Django 1.4 provides the expected behaviour in the original question (assuming you are using Posgtres):
https://docs.djangoproject.com/en/1.4/ref/models/querysets/#distinct

Django order by field comparison

I need to order_by a field comparison such that all fields matching a certain value are displayed at the top.
The SQL to do this is SELECT * FROM messages ORDER BY message='alsfkjsag' DESC
There are at least two ways to do it:
Custom SQL with UNION:
combine two selects
one who contains all rows which have your desired message
the other with all rows who have another message
Add a dynmaic Field to the QuerySet
extra(select={"is_message":"message='alsfkjsag'"})
and then order_by('is_message')
or in short:
Messages.objects.extra(select={"is_message":"message='alsfkjsag'"})
.order_by('is_message')