Question

I'm currently building a web application that will have a friends feature (e.g. Facebook, etc.).

When a friend sends a friend request, a friendship object is created with accepted = False. accepted is set to True when the requested friend accepts the request.

Here are my models (simplified greatly):

class Friendship(models.Model):
    sender = models.ForeignKey('Person', related_name='sender')
    receiver = models.ForeignKey('Person', related_name='receiver')
    accepted = models.BooleanField(default=False)

class Person(models.Model):
    person = models.OneToOneField(User)
    friends = models.ManyToManyField('self', through=Friendship)

I have it like this so I can also manage friend requests.

Now let's say I have a person John. I want to get all of John's friends (e.g., all friends that have accepted John's friend requests, and all friends whose friend requests John has accepted).

Here's how I'm doing it now:

def friends(person):
    friends = Friendship.objects.filter(sender=person, accepted=True) | Friendship.objects.filter(receiver=person, accepted=True)
    friends = [friendship.sender for friendship in friends if friendship.sender != person] + \
              [friendship.receiver for friendship in friends if friendship.receiver != person]
    return friends

To me this seems really ugly and probably slow and inefficient. Does Django have some way of doing something like this?

def friends(person):
    return person.friends.filter(accepted=True)

Unfortunately this exact code doesn't work. Is there a better way?

Was it helpful?

Solution

I've had to run a very similar query recently. Something like this (or similar) should work:

def friends(person):
    friends = Person.objects.filter(Q(sender__receiver=person, sender__accepted=True) | Q(receiver__sender=person, receiver__accepted=True))
    return friends

You can do a join on a related field using the related_name attribute of the M2M Model, like Person.objects.filter(sender__accepted=True)

A "friend" is the other person in the Friendship relation. I.e. if you are the receiver, the friend is the sender, and vice versa. I'd also rename the related names "sender" and "receiver" and prepend "friend", so the above query is clearer.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top