Grouping by multiple columns in Django - django

I have a model with a few columns:
Model (id, country, city, name, age)
I need to group and sort by a few columns:
SELECT * FROM Model
WHERE country = 'USA'
GROUP BY id, city, age
ORDER BY id, city, age
and I'd like to query the same with Django ORM.
Model.objects.filter(country='USA').group_by('id', 'city', 'age').order_by('id', 'city', 'age')
But this call throws after group_by as it returns None.

Related

django query left join, sum and group by

I have a model:
class Product(models.Model):
name = models.CharField(max_length=100)
class Sales(models.Model):
product_id = models.ForeignKey(Product, on_delete=models.CASCADE, related_name='products')
date = models.DateTimeField(null=True)
price = models.FloatField()
How do I return data as the following sql query (annotate sales with product name, group by product, day and month, and calculate sum of sales):
select p.name
, extract(day from date) as day
, extract(month from date) as month
, sum(s.price)
from timetracker.main_sales s
left join timetracker.main_product p on p.id = s.product_id_id
group by month, day, p.name;
Thanks,
If only ORM was as simple as sql... Spent several hours trying to figuring it out...
PS. Why when executing Sales.objects.raw(sql) with sql query above I get "Raw query must include the primary key"
You can annotate with:
from django.db.models import Sum
from django.db.models.functions import ExtractDay, ExtractMonth
Product.objects.values(
'name',
month=ExtractDay('products__date')
day=ExtractDay('products__date'),
).annotate(
total_price=Sum('products__price')
).order_by('name', 'month', 'day')
Note: Normally one does not add a suffix …_id to a ForeignKey field, since Django
will automatically add a "twin" field with an …_id suffix. Therefore it should
be product, instead of product_id.
Note: The related_name=… parameter [Django-doc]
is the name of the relation in reverse, so from the Product model to the Sales
model in this case. Therefore it (often) makes not much sense to name it the
same as the forward relation. You thus might want to consider renaming the products relation to sales.

How to delete duplicate rows from a table using Django ORM?

I have a database table bad_reviews and a corresponding Django model BadReviews. I want to delete duplicate records based on the fields client_id, survey_id, text, rating, privacy_agreement. I've come up with this query which works:
SELECT br.*
FROM bad_reviews br
JOIN (
SELECT client_id, survey_id, text, rating, privacy_agreement, COUNT(*)
FROM bad_reviews
GROUP BY client_id, survey_id, text, rating, privacy_agreement
HAVING count(*) > 1
) dupes
ON br.client_id = dupes.client_id
AND br.survey_id = dupes.survey_id
AND br.text = dupes.text
AND br.rating = dupes.rating
AND br.privacy_agreement = dupes.privacy_agreement
ORDER BY br.client_id, br.survey_id, br.text, br.rating, br.privacy_agreement, br.id
How to rewrite it using Django ORM?
I hope this will work.
from django.db.models import Count, Subquery
# Equivalent of this query in with Django ORM: SELECT client_id, survey_id, text, rating, privacy_agreement, COUNT(*)
# FROM bad_reviews
# GROUP BY client_id, survey_id, text, rating, privacy_agreement
# HAVING count(*) > 1
subquery = BadReviews.objects \
.values('client_id', 'survey_id', 'text', 'rating', 'privacy_agreement') \
.annotate(count=Count('id')).filter(count__gt=1)
# use the subquery as a filter in the main query
bad_reviews = BadReviews.objects.filter(
client_id=Subquery(subquery.values('client_id')),
survey_id=Subquery(subquery.values('survey_id')),
text=Subquery(subquery.values('text')),
rating=Subquery(subquery.values('rating')),
privacy_agreement=Subquery(subquery.values('privacy_agreement')),
).order_by('client_id', 'survey_id', 'text', 'rating', 'privacy_agreement', 'id')

Django fetch manytomany values as list

class Groups:
name = models.CharField(max_length=100)
class User:
firstname = models.CharField(max_length=100)
lastname = models.CharField(max_length=100)
group = models.ManyToManyField(Groups)
i need user details with groupname as list not as separate records.
User.objects.values('firstname','lastname', 'group__name')
While i'm querying like above, i'm getting like this
<QuerySet [{'firstname': 'Market', 'lastname': 'Cloud', 'group__name':
'Group 5'}, {'firstname': 'Market', 'lastname': 'Cloud',
'group__name': 'Group 4'}]>
but i want like this
<QuerySet [{'firstname': 'Market', 'lastname': 'Cloud', 'group__name':
['Group 5', 'Group 4']}]>
is there a way, i can query like this without doing separate query.
If you're using postgres you can use ARRAY_AGG function. In Django ORM like below:
from django.contrib.postgres.aggregates import ArrayAgg
User.objects \
.annotate(list_of_group_names=ArrayAgg('group__name')) \
.order_by('id').distinct() \
.values('firstname', 'lastname', 'list_of_group_names')
Note: distinct is useful, because joining tables can result in duplicates

Group by column Django orm

I have a database table users that consists of 4 columns id,name,country,state I will like to run an sql query like
SELECT country, state, name, COUNT(id) from users GROUP BY country, state, name
please how do i accomplish this using Django ORM
You can construct a QuerySet with:
from django.db.models import Count
qs = Users.objects.values('country', 'state', 'name').annotate(
count=Count('pk')
).order_by('country', 'state', 'name')
This will construct a queryset that is a collection of dictionaries, where each dictionary has four keys: country, state, name and count. The count is the total number of Users with the given name, state and country combination.

Django Queryset - extracting only date from datetime field in query (inside .value() )

I want to extract some particular columns from django query
models.py
class table
id = models.IntegerField(primaryKey= True)
date = models.DatetimeField()
address = models.CharField(max_length=50)
city = models.CharField(max_length=20)
cityid = models.IntegerField(20)
This is what I am currently using for my query
obj = table.objects.filter(date__range(start,end)).values('id','date','address','city','date').annotate(count= Count('cityid')).order_by('date','-count')
I am hoping to have a SQL query that is similar to this
select DATE(date), id,address,city, COUNT(cityid) as count from table where date between "start" and "end" group by DATE(date), address,id, city order by DATE(date) ASC,count DESC;
At least in Django 1.10.5, you can use something like this, without extra and RawSQL:
from django.db.models.functions import Cast
from django.db.models.fields import DateField
table.objects.annotate(date_only=Cast('date', DateField()))
And for filtering, you can use date lookup (https://docs.djangoproject.com/en/1.11/ref/models/querysets/#date):
table.objects.filter(date__date__range=(start, end))
For the below case.
select DATE(date), id,address,city, COUNT(cityid) as count from table where date between "start" and "end" group by DATE(date), address,id, city order by DATE(date) ASC,count DESC;
You can use extra where you can implement DB functions.
Table.objects.filter(date__range(start,end)).extra(select={'date':'DATE(date)','count':'COUNT(cityid)'}).values('date','id','address_city').order_by('date')
Hope it will help you.
Thanks.