Reputation: 3567
I have a following models for simple application:
class App(models.Model):
name = models.CharField(max_length=60)
class TestCase(models.Model):
title = models.CharField(max_length=500)
app = models.ForeignKey('apps.App', blank=True)
class Run(models.Model):
timestamp = models.DateTimeField()
value = models.FloatField()
test_case = models.ForeignKey(TestCase)
And for example I want to calculate for executed and total cases in specific run:
>>> # r is instance of Run
>>> cases_ids = r.values('test_case').distinct()
>>> executed_cases = TestCase.objects.filter(pk__in=cases_ids)
>>> executed_cases_stat = executed_cases.values('app__name').annotate(dcound=Count('app__name'))
[{'executed_cases': 1, 'app__name': u'App1'}, {'executed_cases': 3, 'app__name': u'App2'}]
>>> all_cases_stat = TestCase.objects.all().values('app__name').annotate(total_cases='app__name')
[{'total_cases': 5, 'app__name': u'App1'}, {'total_cases': 7, 'app__name': u'App2'}, {'total_cases': 12, 'app__name': u'App3'}]
>>>
>>> # I want to get this result:
[{'executed_cases': 1, 'total_cases': 5, 'app__name': u'App1'}, {'executed_cases': 3, 'total_cases': 7, 'app__name': u'App2'}, {'executed_cases': 0, 'total_cases': 12, 'app__name': u'App3'}]
I know that it's possible via SQL(it may looks ugly):
select
simple_app.name
, tc.total_cases
, ce.cases_executed
from apps_app
join (
select count(id) as total_cases, app_id
from simple_testcase
group by app_id) tc
on simple_app.id = tc.app_id
join(
select count(id) as cases_executed, app_id
from simple_testcase
where id in (select test_case_id
from simple_run where run_id = 1)
group by app_id) ce
on apps_app.id = ce.app_id
Is there any way do it without raw sql or iterating in python?
Upvotes: 1
Views: 656
Reputation: 15084
Most of the times I want to do things that seem impossible with annotations, I use extra
(https://docs.djangoproject.com/en/dev/ref/models/querysets/#django.db.models.query.QuerySet.extra)
for instance, in your case you could do something like this:
apps_extra = App.objects.all().extra (select={ 'total_cases': """ select count(id) from simple_testcase where simple_app.id = simple_testcase.app_id """, 'executed_cases': """ # the same with your second nested query """, })
Now the apps_extra
will contain App
objects with total_cases
and executed_cases
fields !
This is really great because you can use apps_extra
as it is as a Column()
source to django_tables2
and enable sorting on that column !
Upvotes: 3