Creating a follow and unfollow system in Django | python
Tags: python, django, webdev, programming
Published: 2021-04-25 08:38:48

Social Media giants like Instagram, Twitter, Facebook, and Github have one thing in common, that is it follow/unfollow feature. which allows the user to follow each other.

Here, in this post, we have tried to mimic this feature.
*Basic knowledge of Django is required


1.  Modules required :
 - Django : 
```
pip install Django
``` 

2. Basic setup :
 - Command to make a project - here, the name of the project is named as "blog"

     ```
       django-admin startproject blog 
     ``` 

 - In blog folder create an app - here, the name of the app is  named as  "author"

      ``` 
       python manage.py startapp author
      ``` 
3. code
 - After creating the author app, open the models.py file in the author
 
```
from django.db import models
from django.contrib.auth.models import AbstractUser

class User(AbstractUser):
    following = models.ManyToManyField(
        "self", blank=True, related_name="followers", symmetrical=False
    )
    bio = models.TextField(max_length=200, blank=True, default="Bio")
    website = models.URLField(max_length=200, blank=True)


    def __str__(self):
          return f"{self.username} Profile"

``` 
Here, the ManyToMany field will help us create a followings field as a user can follow many other users and many users can follow him.
 [Docs on ManyTOManyField](https://docs.djangoproject.com/en/3.2/topics/db/examples/many_to_many/)

 - After creating models and running migrations, we need to work on views.py 


```
from django.shortcuts import render
from django.http import HttpResponseRedirect
from django.urls import reverse
from author.models import User


def profile(request, username):
    userProfile = User.objects.get(username=username)

    data = {
        "author": userProfile,
    }
    return render(request, "author/profile.html", data)


def followToggle(request, author):
    authorObj = User.objects.get(username=author)
    currentUserObj = User.objects.get(username=request.user.username)
    following = authorObj.following.all()

    if author != currentUserObj.username:
        if currentUserObj in following:
            authorObj.following.remove(currentUserObj.id)
        else:
            authorObj.following.add(currentUserObj.id)

    return HttpResponseRedirect(reverse(profile, args=[authorObj.username]))
``` 

we have to functions here: 

 1.  profile() - this function handle user profile by accepting username as an argument
  
 2.  followToggle() - this function handle follow/unfollow system for a user. This function takes a username as an argument, which our current user wants to follow/unfollow.

- Now time to work on urls.py to connect views


```
from django.urls import path
from author import views

urlpatterns = [
    path("profile/<str:username>/", views.profile, name="profile"),
    path("followToggle/<str:author>/",views.followToggle, name="followToggle")
]
``` 


- Create a templates folder inside the templates folder, create an author/profile.html template.

*I have use bootstrap for my project. so, please ignore classes

```
{% extends "base.html" %}
{% load static %}
{% block header %}
{% endblock header %}

{% block content %}

<div class="pt-5 mt-5 pb-5 mb-5">
  <div class="container">
    <div class="main-body">
      <div class="row gutters-sm">
        <div class="col-md-4 mb-3">
          <div class="card">
            <div class="card-body">
              <div class="d-flex flex-column align-items-center text-center">
                <img src="{{ author.image.url }}" alt="{{ author.username }}" class="rounded-circle" width="150" />
                <div class="mt-3">
                  <h4>{{ author.username }}</h4>
                  <div class="row">
                    <b> following : </b>
                    <p class="text-muted"> {{ author.following.count }} </p>
                    &nbsp;&nbsp;&nbsp;&nbsp;
                    <b> followers : </b>
                    <p class="text-muted"> {{ author.followers.count }} </p>
                  </div>
                  <h6 class="text-muted font-size-sm">
                    {{ author.bio }}
                  </h6>
                  {% if user.is_authenticated %}
                    {% if user in author.following.all %}
                        <a href="{% url 'followToggle' author=author.username  %}" class="btn btn-primary">Unfollow</a>
                      {% else %}
                        <a  href="{% url 'followToggle' author=author.username  %}" class="btn btn-primary"> Follow </a>
                    {% endif %}
                      <button class="btn btn-outline-primary">Message</button>
                  {% else %}
                    <p class="text-muted"> please, login to follow </p>
                  {% endif %}
                </div>
              </div>
            </div>
          </div>
          
          <div class="card mt-3">
            <ul class="list-group list-group-flush">
              {% if author.website %}
              <li class="list-group-item d-flex justify-content-between align-items-center flex-wrap">
                <h6 class="mb-0">
                  <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"
                    stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
                    class="feather feather-globe mr-2 icon-inline">
                    <circle cx="12" cy="12" r="10"></circle>
                    <line x1="2" y1="12" x2="22" y2="12"></line>
                    <path
                      d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z">
                    </path>
                  </svg>Website
                </h6>
                <span class="text-secondary">{{ author.website }}</span>
              </li>
              {% endif %}
            </div>

          </div>
        </div>
      </div>
    </div>
</div>


{% endblock content %}
```
 
 
this is the profile page for the author we want to follow/unfollow, we get this page by passing "username" to **profile views**  
here, 
 - `{{ author.following.count }}` : this tag represents the count of users following the current user 
 - `{{ author.followers.count }}`: this tag represents the count of users followed by the current user, this type of relationship tag is called reverse or backward relationship [ docs](https://docs.djangoproject.com/en/dev/topics/db/queries/#many-to-many-relationships)
 
To follow/unfollow this user we are using `{% url 'followToggle' author=author.username  %}` this URL tag. follow and unfollow text on the button is represented by looking if the user exits in the author following list

That's it. hope, you like it.