views:

218

answers:

2

I have a model called Item, with m2m relation to User ("owner").

For each item, i need to count users who own it. That's easy enough with annotate()

But then i need to calculate ratio between owners of specific gender and total owner count for each item. For example if, 2 males own the item out of 5 users, the ration is 0.4.

What's the best way to do that?

A: 

Just on top of my head, something like the following could work. Iterate on it to get your perfect solution if you wish:

items = Item.objects.annotate(Count('users'))

for item in items:
    total = item.users__count
    num_males = item.users.filter(gender='M').count()
    num_females = item.users.filter(gender='F').count()
Thierry Lam
i thought about that first, but it's so heavy on DB calls :/
Dmitry Shevchenko
If the above can be done in pure SQL with less DB calls than the above, then use raw SQL or post it here so that someone can find the equivalent QuerySet.
Thierry Lam
+3  A: 

To do this with the ORM, you need conditional aggregates, which aren't supported in Django. http://www.voteruniverse.com/Members/jlantz/blog/conditional-aggregates-in-django proposes a hacky solution that might work.

If you don't need to sort by the ratio, then you can make two calls to annotate, and then compute the ratio in Python. Something like:

items = Item.objects.annotate(ucount=Count('users')).annotate(ccount=CountIf(<condition>))
for item in items:
    item.ratio = item.ucount / item.ccount

If you don't want to do that, I'd recommend using the extra() method and some custom sql to get the extra info you want. Documentation for that method is on the Django Queryset API documentation page.

andylei