Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django Aggregation Across Reverse Relationship

Given these two models:

class Profile(models.Model):
    user = models.ForeignKey(User, unique=True, verbose_name=_('user'))
    about = models.TextField(_('about'), blank=True)
    zip = models.CharField(max_length=10, verbose_name='zip code', blank=True)
    website = models.URLField(_('website'), blank=True, verify_exists=False)

class ProfileView(models.Model):
    profile = models.ForeignKey(Profile)
    viewer = models.ForeignKey(User, blank=True, null=True)
    created = models.DateTimeField(auto_now_add=True)

I want to get all profiles sorted by total views. I can get a list of profile ids sorted by total views with:

ProfileView.objects.values('profile').annotate(Count('profile')).order_by('-profile__count')

But that's just a dictionary of profile ids, which means I then have to loop over it and put together a list of profile objects. Which is a number of additional queries and still doesn't result in a QuerySet. At that point, I might as well drop to raw SQL. Before I do, is there a way to do this from the Profile model? ProfileViews are related via a ForeignKey field, but it's not as though the Profile model knows that, so I'm not sure how to tie the two together.

As an aside, I realize I could just store views as a property on the Profile model and that may turn out to be what I do here, but I'm still interested in learning how to better use the Aggregation functions.

like image 605
Tom Avatar asked May 04 '10 15:05

Tom


2 Answers

ProfileViews are related via a ForeignKey field, but it's not as though the Profile model knows that

The Profile model actually does know that. You should be able to do something like:

  Profile.objects.all().annotate(count_views=Count('profileview__pk')).order_by('count_views')

Edit: Here you can find Django docs about lookups that span relationships http://docs.djangoproject.com/en/dev/topics/db/queries/#lookups-that-span-relationships

like image 51
Botond Béres Avatar answered Sep 24 '22 14:09

Botond Béres


Surely this will work:

Profile.objects.all().annotate(viewcount=Count('profileview')).order_by('-viewcount')

As Botondus says, the Profile model is aware of the backwards ForeignKey relationship.

like image 43
Daniel Roseman Avatar answered Sep 21 '22 14:09

Daniel Roseman