Django: Order items according to count of intersecting objects (mutual friends)
I'm using Django to write a social networking application, and need to implement a feature similar to the Facebook "Mutual Friends" concept. I have a simple model like this:
class Friend(models.Model):
user = models.ForeignKey(User)
guid = models.BigIntegerField()
name = models.TextField()
class Meta:
unique_together = ['user', 'facebook_id']
It represents a relationship between a user of my site, and one of his friends. (The friends are not necessarily also users) The number of mutual 开发者_开发百科friends between two users can be calculated as the intersection of their friend lists, in other words,
users = User.objects.all()
friends = Friend.objects.filter(user=request.user)
friend_ids = [f.guid for f in friends]
for user in users:
user.mutual = Friend.objects.filter(user=user, guid__in=friend_ids).count()
(A more efficient way of doing the above will score bonus points).
My main question is, having calculated the number of mutual friends between the users, how can I now order the users
queryset according to number of mutual friends of the current user? I cannot, in this case, save the count as a annotation or extra field, since it relies on the specific user being examined and only a certain subset of his total friends. Can I use the annotate
or extra
methods in some ingenious way? Or is a raw
sql query the only way to go? If so, how?
To summarize:
Calculating the number of mutual friends for each user is not the problem. Given that information for each user, how do you then proceed to order the QuerySet
according to that number?
Not sure if this is exactly what you are looking for but...
# get current users friends (assuming guid is unique for a friend?)
user_friend_guids = Friend.objects.values_list('guid', flat=True).filter(user=user)
# get Friend objects where user not current user, is in user_friend_list, group and count by user
mutual_friends = Friend.objects.values('user__username') \
.filter(guid__in=user_friend_guids).exclude(user=user) \
.annotate(number_mutual_friends=Count('user')) \
.order_by('-number_mutual_friends')
this would return a list of usernames with the amount of friends they share with user, ordered by the number they share.
then in the template:
{% for mutual_friend in mutual_friends %}
{{mutual_friend.user__username}} - {{mutual_friends.number_mutual_friends}}
{% endfor %}
精彩评论