开发者

Help Translate this query into Django ORM

I have a Friendship model with two related user objects associated with it. I would like to write a method that takes a user and returns a list of that user's friends. I was doing it via:

 friends = Friendship.objects.filter(Q(user1=user) | Q(user2=user))
 friends_list = [f.user1 if user == f.user2 else f.user2 for f in friends]

but this incurs a query for every user that is returned in the query set (e.g. hundreds of queries). I could write it via:

 friends = Friendship.objects.select_related().filter(Q(user1=user) | Q(user2=user))
 friends_list = [f.user1 if user == f.user2 else f.user2 for f in friends]

but this does an INNER JOIN on the user table. I could also write it via custom SQL,

 friends = User.objects.raw("""
            SELECT * FROM usertable WHERE id IN (SELECT
            user1_id FROM friendstable WHERE user2_id=%d) OR id IN 
            (SELECT user2_id FROM lists_friendship WHERE user1_id=%d);
            """ % (user.pk, user.pk))

but I was thinking 开发者_C百科there must a way to do it inside the ORM without all those extra lookups. I was trying to do something with id__in, but I couldn't find a way to get the user ids out of the friends query set.


Assuming you set up your Friendship model similar to the example below and use friend_set as the related name of the from_friend field in your model, getting a list of a users friends can be as simple as a basic list comprehension:

friends = [friendship.to_friend for friendship in user.friend_set.all()]

This would even allow you to access a user's friends in your template without having to pass that as a variable:

{% for friendship in user.friend_set.all %}
    {{ friendship.to_friend.username }}
{% endfor %}

Your Friendship model would look like this:

class Friendship(models.Model):
  from_friend = models.ForeignKey(
    User, related_name='friend_set'
  )
  to_friend = models.ForeignKey(
    User, related_name='to_friend_set'
  )
  def __unicode__(self):
    return u'%s, %s' % (
      self.from_friend.username,
      self.to_friend.username
    )
  class Meta:
    unique_together = (('to_friend', 'from_friend'), )

Here's a great article on building friend networking in Django: http://www.packtpub.com/article/building-friend-networks-with-django-1.0

Note that you shouldn't have to check both from_friend and to_friend to get a users friend list. If you have a following/follower friendship model you only need all friendship instances with a from_friend = user, and if you have a double opt-in friendship model you could follow what Facebook does and add an instance for the invited friend as the from_friend once they accept the friend invite.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜