1

I have a queryset at the moment that returns the number items in a model ordered by the number of people watching it. So I have a m2m field representing this link.

Ie:

#models.py
class MyModel(models.Model):
...
watchers = models.ManyToManyField(User, blank=True)

I would count the number of occurrences and order them by count in a default manager which was then used by the view.

Now I'm going over to using django-notification, using 'notification.ObservedItem' to allow users to watch an instance if MyModel.

So in my view when a user posts some content I have something like this:

notification.observe(object, request.user, 'new_object')

This works well.

Now, how can I generate a queryset representing all the objects of class MyModel, ordered by the number of people 'observing' them?

powlo
  • 2,538
  • 3
  • 28
  • 38

2 Answers2

1

You can accomplish that by using annotations:

from django.db.models import Count
MyModel.objects.annotate(num_users=Count('watchers')).order_by('num_users')
Gonzalo
  • 4,145
  • 2
  • 29
  • 27
  • You're right, I think the annotation should happen on MyModel instead, editing answer. – Gonzalo Jul 27 '12 at 15:05
  • Holy smoke you're right. I'm confused why the documentation says you can't do it when it seems like you can. – powlo Jul 27 '12 at 16:15
  • FWIW, I tested my code with your first model implementation (watchers being a M2M) and worked well :) – Gonzalo Jul 27 '12 at 16:39
  • Yeah I had it working with the standard m2m, but that was before the transition to django-notification. – powlo Jul 27 '12 at 17:14
  • Ah, didn't know you removed the m2m. In that case I guess you have two choices: bring back the m2m and attach a signal handler to `observe' that adds the user instance to it, or try django-generic-aggregation. – Gonzalo Jul 27 '12 at 17:56
1

The problem is that django-notification uses generic foreign keys.

So I've redefined my watchers field:

watchers = generic.GenericRelation(notification.ObservedItem)

Then I can get all the watchers for a specific instance of MyModel.

$x = MyModel.objects.get(id=1)
$x.watchers.all()
$[<ObservedItem: ObservedItem object>, <ObservedItem: ObservedItem object>, <ObservedItem: ObservedItem object>]
$x.watchers.count()
$3

Close but no cigar. I wanted to do was something like this:

MyModel.objects.annotate(count=Count('watchers')).order_by('count')

Which is something that Django can't do, according to the docs.

Django's database aggregation API doesn't work with a GenericRelation.

Not to worry, I think this will probably help:

http://charlesleifer.com/blog/generating-aggregate-data-across-generic-relations/

Repo is here:

https://github.com/coleifer/django-generic-aggregation

I've not tried it out yet...

Community
  • 1
  • 1
powlo
  • 2,538
  • 3
  • 28
  • 38