Best Models for a Friendship relation (in Django)
Asked Answered
H

3

9

What is the best way to model friendships between users for a social networking site?

The possible states are:

  • no Friendship
  • FriendRequest from A to B, B needs to confirm (this is asymmetric)
  • A and B are friends (this is symmetric)

now its complicated to choose the models right.

My Friends are part of my profile

quite obvious, A.profile.friends is a many to many relation to other users.

  • no friendship: B not in A.friends and A not in B.friends
  • A requests friendship with B: B in A.friends
  • Friends: B in A.friends and A in B.friends

but it seems to be rather unclean to merge the friend with the friendrequest relation. and without this merge, the data is redundant, because then "A in B.friends and not B in A.friends" would be an undefined state.

Friend-Lookup: A.friends.filter(friends__contains=B) #rather complicated lookup on db level, unintuitive for coders

Seperate tables

FriendRequest is quite obvious, a class with requester and requested_user, the selects are quite obvious, too.

The Friend Model would be not very nice, because it would have person1 and person2 as fields, and all lookups need to select Friends with person1=A and person2=B OR person1=B and person2=A

Friend-Lookup: Friend.objects.filter(person1=A) union Friend.objects.filter(person2=A) #unclean with the need to union two sets

Seperate many2many table

another option would be a Friend model with a friends field, which is a many2many field, which links to exactly two persons. Then the select is matching one of the persons in the friends field, and then just returns the model, where the person B can be extrated by substracting A from the friends set. But this would be overkill, because no friend-object would ever have more than 2 persons associated.

Friend-Lookup: Friendship.objects.filter(persons__contains=A) #queries two tables

So, what do you think is the cleanest and most intuitive solution to storing a friendship-relation? Are there any common patterns how to do it?

Hetero answered 25/12, 2011 at 19:28 Comment(2)
Here you have a good example of your third option github.com/revsys/django-friendship/blob/master/friendship/…Radiosonde
This looks like the thing I looked for in 2011. The project where I needed it is now abandoned, but possibly I could have used it. If you like to explain some of the core aspects of these models (link only answers are not welcome here) in an answer I can accept it.Hetero
R
2

If you do not want to re-implement all these Friendship relation stuff, you can use the following module: https://github.com/revsys/django-friendship

It's behavior is what you describe in your third option: it creates separate ManyToMany tables. One for the friendship request:

class FriendshipRequest(models.Model):
    """ Model to represent friendship requests """
    from_user = models.ForeignKey(AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='friendship_requests_sent')
    to_user = models.ForeignKey(AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='friendship_requests_received')

An other one for the friendship status:

class Friend(models.Model):
    """ Model to represent Friendships """
    to_user = models.ForeignKey(AUTH_USER_MODEL, models.CASCADE, related_name='friends')
    from_user = models.ForeignKey(AUTH_USER_MODEL, models.CASCADE, related_name='_unused_friend_relation')

It also provides follow, block and related managers.

Radiosonde answered 21/10, 2018 at 13:2 Comment(0)
M
1

I believe this is a use case for the extended many-to-many relationship supported by Django: https://docs.djangoproject.com/en/dev/topics/db/models/#intermediary-manytomany

Instead of just storing the connection between those users, you can store additional properties. This should project your problem domain onto the DB model pretty well. Ie create your connection as soon as one of the two initiates the friendship, then set additional fields to store who's asking whom and whether the other person has accepted the friendship.

Mineralize answered 25/12, 2011 at 20:36 Comment(1)
yeah, that is the third option is mentioned, something like FriendShip.persons=models.Many2ManyField(Person), with the possibility to store additional shared data in the same model. The access is completely symmetric, but it uses one table for Friendship-Objects and one Table for the m2m-Field, which has two entries per Friendship object, which is not as efficient as for example Friendship(personA, personB).Hetero
A
0

For an sql db, I would use many to many relationship for this. But if you think that you will have a lot of users You might want to consider graph databases like flock-db which is specifically designed for this kind of data

http://en.wikipedia.org/wiki/Graph_database

(Keep your regular data on sql db and keep relationships on graph database)

Ammadis answered 25/12, 2011 at 20:32 Comment(1)
Interesting option, as Friendships are a good use case for graphs. But for the moment is would like to have a efficient and more important clean solution for doing it with django models (which translate to SQL in some way).Hetero

© 2022 - 2024 — McMap. All rights reserved.