5 min read

An important aspect of socializing in our application is letting users to maintain their friend lists and browse through the bookmarks of their friends. So, in this section we will build a data model to maintain user relationships, and then program two views to enable users to manage their friends and browse their friends’ bookmarks.

Creating the friendship data model

Let’s start with the data model for the friends feature. When a user adds another user as a friend, we need to maintain both users in one object. Therefore, the Friendship data model will consist of two references to the User objects involved in the friendship. Create this model by opening the bookmarks/models.py file and inserting the following code in it:

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'), )



The Friendship data model starts with defining two fields that are User objects: from_friend and to_friend. from_friend is the user who added to_friend as a friend. As you can see, we passed a keyword argument called related_name to both the fields. The reason for this is that both fields are foreign keys that refer back to the User data model. This will cause Django to try to create two attributes called friendship_set in each User object, which would result in a name conflict. To avoid this problem, we provide a specific name for each attribute. Consequently, each User object will contain two new attributes: user.friend_set, which contains the friends of this user and user.to_friend_set, which contains the users who added this user as a friend. Throughout this article, we will only use the friend_set attribute, but the other one is there in case you need it .

Next, we defined a __unicode__ method in our data model. This method is useful for debugging.

Finally, we defined a class called Meta. This class may be used to specify various options related to the data model. Some of the commonly used options are:

db_table : This is the name of the table to use for the model. This is useful when the table name generated by Django is a reserved keyword in SQL, or when you want to avoid conflicts if a table with the same name already exists in the database.

: This is the name of the table to use for the model. This is useful when the table name generated by Django is a reserved keyword in SQL, or when you want to avoid conflicts if a table with the same name already exists in the database. ordering : This is a list of field names. It declares how objects are ordered when retrieving a list of objects. A column name may be preceded by a minus sign to change the sorting order from ascending to descending.

: This is a list of field names. It declares how objects are ordered when retrieving a list of objects. A column name may be preceded by a minus sign to change the sorting order from ascending to descending. permissions : This lets you declare custom permissions for the data model in addition to add, change, and delete permissions. Permissions should be a list of two-tuples, where each two-tuple should consist of a permission codename and a human-readable name for that permission. For example, you can define a new permission for listing friend bookmarks by using the following Meta class: class Meta:

permissions = (

('can_list_friend_bookmarks',

'Can list friend bookmarks'),

)



: This lets you declare custom permissions for the data model in addition to add, change, and permissions. Permissions should be a list of two-tuples, where each two-tuple should consist of a permission codename and a human-readable name for that permission. For example, you can define a new permission for listing friend bookmarks by using the following class: unique_together: A list of field names that must be unique together.

We used the unique_together option here to ensure that a Friendship object is added only once for a particular relationship. There cannot be two Friendship objects with equal to_friend and from_friend fields. This is equivalent to the following SQL declaration:

UNIQUE ("from_friend", "to_friend")



If you check the SQL generated by Django for this model, you will find something similar to this in the code.

After entering the data model code into the bookmarks/models.py file, run the following command to create its corresponding table in the database:

$ python manage.py syncdb



Now let’s experiment with the new model and see how to store and retrieve relations of friendship. Run the interactive console using the following command:

$ python manage.py shell



Next, retrieve some User objects and build relationships between them (but make sure that you have at least three users in the database):

>>> from bookmarks.models import *

>>> from django.contrib.auth.models import User

>>> user1 = User.objects.get(id=1)

>>> user2 = User.objects.get(id=2)

>>> user3 = User.objects.get(id=3)

>>> friendship1 = Friendship(from_friend=user1, to_friend=user2)

>>> friendship1.save()

>>> friendship2 = Friendship(from_friend=user1, to_friend=user3)

>>> friendship2.save()



Now, user2 and user3 are both friends of user1. To retrieve the list of Friendship objects associated with user1, use:

>>> user1.friend_set.all()

[ , ]



(The actual usernames in output were replaced with user1, user2, and user3 for clarity.)

As you may have already noticed, the attribute is named friend_set because we called it so using the related_name option when we created the Friendship model.

Next, let’s see one way to retrieve the User objects of user1’s friends:

>>> [friendship.to_friend for friendship in

user1.friend_set.all()]

[ , ]



The last line of code uses a Python feature called “list” comprehension to build the list of User objects. This feature allows us to build a list by iterating through another list. Here, we built the User list by iterating over a list of Friendship objects. If this syntax looks unfamiliar, please refer to the List Comprehension section in the Python tutorial.

Notice that user1 has user2 as a friend, but the opposite is not true.

>>> user2.friend_set.all()

[]



In other words, the Friendship model works only in one direction. To add user1 as a friend of user2, we need to construct another Friendship object.

>>> friendship3 = Friendship(from_friend=user2, to_friend=user1)

>>> friendship3.save()

>>> user2.friend_set.all()

[ ]



By reversing the arguments passed to the Friendship constructor, we built a relationship in the other way. Now user1 is a friend of user2 and vice-versa. Experiment more with the model to make sure that you understand how it works. Once you feel comfortable with it, move to the next section, where we will write views to utilize the data model. Things will only get more exciting from now on!