I am trying to get all the last records based on the id, sorted by the month.
this gives me the last record,
qs = Cashflow.objects.filter ( acct_id = a.id ).order_by('-month')[:1]
And this groups the accounts,
qs = Cashflow.objects
.filter ( acct_id = a.id )
.values ( 'acct_id' )
.annotate ( count = Count ( 'acct_id' ) )
.values ( 'acct_id', 'count' )
.order_by ( )
How how can I combine the two queries into one?
Group by acct_id, sort by "month" and get last record.
is this even possible? thanks
EDIT:
this is the sql version of what I am trying to do.
select *
from cashflow t
inner join (
select acct_id, max ( `month` ) as MaxDate
from cashflow
where acct_id IN ( 1,2,3,... )
group by acct_id
) tm on t.acct_id = tm.acct_id and t.month = tm.MaxDate
order by acct_id
Can this be done in pure Django of should I just do a Raw query?
cheers.
Best solution I found online https://gist.github.com/ryanpitts/1304725
'''
given a Model with:
category = models.CharField(max_length=32, choices=CATEGORY_CHOICES)
pubdate = models.DateTimeField(default=datetime.now)
<other fields>
Fetch the item from each category with the latest pubdate.
'''
model_max_set = Model.objects.values('category').annotate(max_pubdate=Max('pubdate')).order_by()
q_statement = Q()
for pair in model_max_set:
q_statement |= (Q(category__exact=pair['category']) & Q(pubdate=pair['max_pubdate']))
model_set = Model.objects.filter(q_statement)
Instead .order_by('-month')[:1] it's better to use .order_by('month').last() or .order_by('-month').first() (or earliest/latest for dates).
Of course when grouping you can use order_by:
last_record = Cashflow.objects \
.values('acct_id') \
.annotate(count=Count('id')) \
.order_by('month') \
.last()
Related
I have Users table containing user_email, user_name, user_category.
The following DAX filter returns a table:
FILTER(Users,[User_Email] = userprincipalname())
I want to get the user_category.
1 approach is SELECTEDCOLUMNS( FILTER(Users,[User_Email] = userprincipalname()), "User_category", [User_Category] ). This returns the result as a column.
Is there any alternate approach to return just 1 value? For example:
SELECTEDVALUE ( SELECTEDCOLUMNS( FILTER(Users,[User_Email] = userprincipalname()), "User_category", [User_Category] ) )
OR
VALUES ( SELECTEDCOLUMNS( FILTER(Users,[User_Email] = userprincipalname()), "User_category", [User_Category] ) )
You can use MAXX on the table generated by FILTER.
MAXX(
FILTER(Users,[User_Email] = userprincipalname()),
[User_Category]
)
You can do this with CALCULATE assuming you don't expect there to be multiple values to choose from (if there are, this will return a blank).
CALCULATE (
SELECTEDVALUE ( Users[User_Category] ),
FILTER ( Users, Users[User_Email] = userprincipalname() )
)
I want to annotate a count field in my queryset with a subquery filter:
My code:
module_attempts = Subquery(
ModuleProgressAttempt.objects.filter(
module_progress__module__id=OuterRef("pk")
).only("pk")
)
real_instances = VirtualClassRoomModule.objects.filter(
id__in=[vc.id for vc in vc_classrooms]
).annotate(
attendees_count=Count(module_attempts),
)
Here module_progress__module__id is the id of the current VirtualClassRoomModule of the annotation iteration. The count is basically the length of the ModuleProgressAttempt filtered queryset. Currently the count is always 1 , eventhough the
ModuleProgressAttempt.objects.filter(
module_progress__module__id=<Current-module-id>
)
returns more than one object.
This solution worked for me:
module_attempts_count = Subquery(
ModuleProgressAttempt.objects.filter(module_progress__module__id=OuterRef("id"))
.values("module_progress__module__id")
.annotate(cnt=Count("id"))
.values("cnt")
)
real_instances = VirtualClassRoomModule.objects.filter(
id__in=[vc.id for vc in vc_classrooms]
).annotate(
attendees_count=module_attempts_count,
)
I need to get the field "Name" from "Table1" in a calculated column in "Table2", like:
Table1:
Name | Date
ABC | 5-jan-2017
ABC | 7-jan-2017
DEF | 8-may-2018
DEF | 10-jun-2018
And Table2:
Date | CalcColumn
6-Jan-2017 | ABC
25-may-2018 | DEF
The logic is the following:
If Date in Table2 is within the minimum and maximum dates of Table1 then get the name in Table1.
I'd recommend reshaping Table1 so that each Name has two columns, StartDate and EndDate instead of having those in separate rows. If you don't, you can create that table as a variable as follows:
CalcColumn =
VAR Summary =
SUMMARIZE (
Table1,
Table1[Name],
"StartDate", MIN ( Table1[Date] ),
"EndDate", MAX ( Table1[Date] )
)
RETURN
MAXX (
FILTER (
Summary,
Table2[Date] >= [StartDate] &&
Table2[Date] <= [EndDate] ),
Table1[Name]
)
You only need the part after the RETURN if your data is reshaped.
I am trying to do a query and get a sum of values based on a condition.
I need all the 'shares' when the 'action' == '+'
I have to group by the issues.
qs = Trades.objects.all ().filter ( id = id )
.annotate (
d_shrs = Sum ( When (
action = '+', then = 'shares'
) )
).order_by ( 'issue' )
Where am I going wrong?
You need to use Case (it allows you to use if-else kind of constructs in your queries) with a When, F will get you the actual value of the field
from django.db.models import Case, When, F, Sum
qs = Trades.objects.filter(id=id).annotate(
d_shrs=Sum(
Case(
When (
action='+', then=F('shares')
)
)
)
).order_by('issue')
I have a query in MySql that I need translated into Django ORM. It involves joining on two tables with two counts on one of the tables. I'm pretty close to it in Django but I get duplicate results. Here's the query:
SELECT au.id,
au.username,
COALESCE(orders_ct, 0) AS orders_ct,
COALESCE(clean_ct, 0) AS clean_ct,
COALESCE(wash_ct, 0) AS wash_ct
FROM auth_user AS au
LEFT OUTER JOIN
( SELECT user_id,
Count(*) AS orders_ct
FROM `order`
GROUP BY user_id
) AS o
ON au.id = o.user_id
LEFT OUTER JOIN
( SELECT user_id,
Count(CASE WHEN service = 'clean' THEN 1
END) AS clean_ct,
Count(CASE WHEN service = 'wash' THEN 1
END) AS wash_ct
FROM job
GROUP BY user_id
) AS j
ON au.id = j.user_id
ORDER BY au.id DESC
LIMIT 100 ;
My current Django query (which brings back unwanted duplicates):
User.objects.annotate(
orders_ct = Count( 'orders', distinct = True )
).annotate(
clean_ct = Count( Case(
When( job__service__exact = 'clean', then = 1 )
) )
).annotate(
wash_ct = Count( Case(
When( job__service__exact = 'wash', then = 1 )
) )
)
The above Django code produces the following query which is close but not right:
SELECT DISTINCT `auth_user`.`id`,
`auth_user`.`username`,
Count(DISTINCT `order`.`id`) AS `orders_ct`,
Count(CASE
WHEN `job`.`service` = 'clean' THEN 1
ELSE NULL
end) AS `clean_ct`,
Count(CASE
WHEN `job`.`service` = 'wash' THEN 1
ELSE NULL
end) AS `wash_ct`
FROM `auth_user`
LEFT OUTER JOIN `order`
ON ( `auth_user`.`id` = `order`.`user_id` )
LEFT OUTER JOIN `job`
ON ( `auth_user`.`id` = `job`.`user_id` )
GROUP BY `auth_user`.`id`
ORDER BY `auth_user`.`id` DESC
LIMIT 100
I could probably achieve it by doing some raw sql subqueries but I would like to remain as abstract as possible.
Based on this answer, you can write:
User.objects.annotate(
orders_ct = Count( 'orders', distinct = True ),
clean_ct = Count( Case(
When( job__service__exact = 'clean', then = F('job__pk') )
), distinct = True ),
wash_ct = Count( Case(
When( job__service__exact = 'wash', then = F('job__pk') )
), distinct = True )
)
Table (after joins):
user.id order.id job.id job.service your case/when my case/when
1 1 1 wash 1 1
1 1 2 wash 1 2
1 1 3 clean NULL NULL
1 1 4 other NULL NULL
1 2 1 wash 1 1
1 2 2 wash 1 2
1 2 3 clean NULL NULL
1 2 4 other NULL NULL
Desired output for wash_ct is 2. Counting distinct values in my case/when, we will get 2.
I think this will work, the chained annotation of job might have produced duplicate users.
If not can you elaborate on duplicates you are seeing.
User.objects.annotate(
orders_ct = Count( 'orders', distinct = True )
).annotate(
clean_ct = Count( Case(
When( job__service__exact = 'clean', then = 1 )
) ),
wash_ct = Count( Case(
When( job__service__exact = 'wash', then = 1 )
) )
)
Try adding values(), also when distinct=True you can combine Count()'s in one annotation().
Users.objects.values("id").annotate(
orders_ct = Count('orders', distinct = True)
).annotate(
clean_ct = Count(Case(When(job__service__exact='clean', then=1)),
distinct = True),
wash_ct = Count(Case(When(job__service__exact='wash',then=1)),
distinct = True)
).values("id", "username", "orders_ct", "clean_ct", "wash_сt")
Using values("id") should add GROUP BY 'id' for annotations and therefore prevent duplicates, see docs.
Also, there's Coalesce, but it doesn't look like it's needed, since Count() returns int anyway. And distinct, but again the distinct in Count() should be enough.
Not sure if Case needed inside Count() as it should count them anyway.