In this lesson, we will present how to implement the follower feature that allows our users to be able to connect with other users. Along with the follower feature, we also implement another feature that users can see only blog posts on the homepage from the users they follow. The steps to implement the Followers feature are as follows:
In previous lessons, we have structured the database model that supports the one-to-one and one-to-many relationships. In this lesson, we will expand the database structure in order to support a more complex relationship — a many-to-many relationship. The Follower feature is a sort of many-to-many relationship in which a user may follow more than one user, and vice versa. Beyond that, this feature is also a self-referential relationship. In Fig. 1, we show the ERD of the Follower feature, and we can see that both sides of the relationship belong to the same entity. In this case, the left-hand side relationship represents the "followers" relationship, and the other side represents the "followed" relationship.
In the "follows" relationship as shown in Fig. 1, we need to store additional information that applies to the link between two entities. We thus add the "date" information to indicate the date a user started following another user. This information also supports ordering lists of followers in a chronological order. We can implement a many-to-many relationship by using an association table which is not as a model and is managed by SQLAlchemy internally - you should refer to Chapter 12 in the textbook . What if we think of this situation in a different way?
Instead of trying to implement the many-to-many relationship in this case, we can decompose this relationship into two one-to-many relationships. In particular, we will construct a new model called
Follow which consists of three attributes, i.e.,
timestamp. This idea is also based on the many-to-many relationships in the conceptual view but represented in a different way in the reality view. In Fig. 2, we show the updates to the app/models.py including (i) the implementation of the "Follows" association table as a model and (ii) a many-to-many relationship implemented as two one- to-many relationships.
To understand the code in Fig. 2, I recommend you to refer to Chapter 12 of the textbook  where the author has a good explanation of those lines of code.
In Fig. 3, we show the implementation of helper functions in the
User model. These functions will allow the application to easily interact with the following-and-followed relationships.
The User Profile of each user in our application needs to be updated to include either the “Follow” or the “Unfollow” button. The state of this button depends on the current state of relationship between the users. We also need to show the number of and the lists of followers and followed users. Fig. 4 shows the changes to the app/templates/user.html to display the following-and-followed relationships.
The updates, as shown in Fig. 4, present four new routes, i.e.,
/followed_by/<username>. The first route will be invoked when a user clicks on the "Follow" button on another user's profile page, and the implementation is shown in Fig. 5. The implementation of the second route is similar to the first one. The third route is quite difficult to implement, in particular, after validating the requested user, it then paginates the followers relationship. The pagination implementation has been introduced in Lesson 5B. Since the query for followers returns a
Follower instance which consists of three attributes as shown in Fig. 2, we need to convert it into a list which has user and timestamp fields in each entry so that rendering on the template is simpler. And the last route,
/followed_by/<username>, is almost similar to the third one, but instead of querying the
user.follower relationship, we just change to the
user.followed. The configuration variable
FLASKY_FOLLOWERS_PER_PAGE = 20 must be defined in the config.py before running the application. The template for both of the third and forth routes need to be implemented in a general way, so that it can be reused, in particular, it just receives the data sent from the view functions (
followed_by()) and renders it on the web page, no matter the relationships a user requests. From Fig. 5 to 9, we show the implementations of the four new routes and the followers.html template.
With the implemented Follower feature, we should customize the homepage for authenticated users that renders blog posts from only the users they follow. Therefore, instead of obtaining all blog posts from the database and rendering them on the web page, we now obtain the list of posts by users followed by the current user, the implementation is as shown in Fig. 10. The query in the function has two steps, as follows:
Followmodels on the condition that
Follow.followed_id == Post.author_id.
Follow.follower_id == self.idwhere
self.ididentifies the current user.
With the implemented helper function in the
User model, the homepage can now give users the ability to view all blog posts or just those from followed users, the implementation is as shown in Fig. 11.
We also implement two new helper functions, as shown in Fig. 11, to set the value of the
show_followed variable. You should note that cookies can only be set on a response object, so we need to manually create a response object via the
make_response() function. The
set_cookie() function takes three parameters, i.e., the cookie's name, the cookie's value, and cookie's lifetime. The
max_age parameter indicates the number of seconds until the cookie expires. In this case, we set the lifetime of
show_followed cookie is 30 days. By default, if we do not set the
max_age parameter, the cookie will expire when the browser is closed.
The homepage template (app/templates/index.html) is updated to include two navigation tabs which represent the /all or /followed routes, the result is as shown in Fig. 12. The source code of the homepage template is shown in Fig. 13.
When you visit the application's homepage and switch to the "Followed" tab, you will notice that your own posts do not appear. Therefore, we need to have a minor update to the source code to render your posts in the "Followed" tab. The simplest way to do this is to register all users as their own followers at the time they create accounts. The implementation is as shown in Fig. 14.
We have a lot of updates to the database model to implement the Follower feature, we thus do update the physical database to include the changes. If the database is small and in the development mode, you can refer to the Running Application section in Lesson 5A. Another way to update the physical database is to implement helper function, as shown in Fig. 15.
After implementing the helper function, the database can be updated by running the following scripts in Flask shell:
$ flask shell
In this lesson, we have discussed the implementation of the Follower feature that allows users to establish a relationship with other users. Based on the relationship, we tweak the homepage to include a navigation tab to filter only blog posts from the following users of the current user. This lesson also presents how to implement the many-to-many relationship and work with cookies in Flask.
/user/<username>route and template to render navigation tabs that allows the current user (when he/she visits his/her User Profile page) to read their blog posts or posts from the users they follow.
In the next lesson, we will implement the last feature of our application - User Comments.