In this tutorial, we'll build a basic social media feed using Django, the Python web framework. A social media feed is a core component of platforms like Facebook, Instagram, and Twitter, allowing users to view and interact with posts, comments, and likes in real-time. By the end of this tutorial, you'll have a fully functional social media feed where users can:
- Signup or Login
- Post, Like & Comment
- Search other users
- Manage profile
Table of Content
- Create a Project Directory and Open in VS Code
- Setting Up Django Project and App
- Install Django
- Create a Django Project
- Create a Django App
- Install and Setup django-bootstrap-v5 Package
- Create urls.py file in "core" app:
- Writing our Logic in the views.py file
- Writing the urls.py file in core app
- Writing Models in models.py
- Writing forms.py file
- Creating all required HTML Templates
- Wrting the admin.py file
- Making Migrations and Creating Super User
- Testing our Social Media Feed
Create a Project Directory and Open in VS Code
Create a Project directory through either your file manager or any terminal interface.

Setting Up Virtual Environment(Optional)
Setting up a virtual environment for your Django app is a good practice as it allows you to isolate dependencies for each project, preventing conflicts between different projects. Here's how you can set up a virtual environment for your Django app:
Install Virtualenv
pip install virtualenv
Create and Activate Virtual Environment
Navigate to your project directory in the terminal and create a virtual environment using virtualenv. Replace myenv with your preferred name for the virtual environment.
virtualenv myenv
myenv\Scripts\activate
Setting Up Django Project and App
Install Django
Once your virtual environment is activated, you can install Django and other dependencies specific to your project using pip:
pip install django
Create a Django Project
After installing Django, you can create your Django project:
django-admin startproject social_media_feedNavigate to Your Project Directory
Move into your project directory:
cd social_media_feedCreate a Django App
After starting project, create a app for our social media feed named as core, you can name your app as per your preference but make sure to change the name in whole project where we use it.
python manage.py startapp coreYour Project Directory should be looking like this now:
Now, Check if you project is properly setup or not by running the project using this command:
python manage.py runserverAfter running this command you should be seeing this output in your browser on http://127.0.0.1:8000/
This tutorial is focusing on creating a Backend using Django so we will not be focusing on UI, but still to have a little aesthetic look we are using Bootstrap5 which is available in django using django-bootstrap-v5 package.
Install and Setup django-bootstrap-v5 Package
Install bootstrap5 using terminal and make sure your Virtual Environment is activated if you are using it.
pip install django-bootstrap-v5Add Bootstrap5 in your Project's settings.py file's INSTALLED_APPS and also add your "core" app in that section:

Now let's start making the actual project. You should know that in your Project's directory 'social_media_feed', there are two directories - 'core' and 'social_media_feed' so here 'social_media_feed' contains main project's setup files like setting.py, urls.py, etc. and 'core' directory contains the our projects HTML template's, models, views, etc.
Create urls.py file in "core" app:
Now add these in project's urls.py:
path('', include('core.urls')),
Writing our Logic in the views.py file
In our social media feed app, there are 13 views that are required for basic feature and those views are:
- Signup View
- Login View
- Logout View
- View for Logged In User Feed and Logged Out User Feed
- View for Creating a Post
- View for Deleting a Post
- View for Commenting on a Post
- View for Deleting a Comment
- View for Liking a Post
- View for showing a user's profile
- View for showing a other user's profile to logged out users
- View for changing a user's username
- View for searching users
# core/views.py
from django.shortcuts import render, redirect, get_object_or_404
from django.contrib.auth.forms import UserCreationForm, AuthenticationForm
from django.contrib.auth import login, logout
from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User
from .models import Post, Comment, Like
from .forms import PostForm, CommentForm, UsernameChangeForm
from django.contrib.auth import update_session_auth_hash
from django.contrib import messages
from django.db.models import Q
# Signup View
def signup(request):
if request.method == 'POST':
form = UserCreationForm(request.POST)
if form.is_valid():
user = form.save()
login(request, user)
return redirect('feed')
else:
form = UserCreationForm()
return render(request, 'signup.html', {'form': form})
# Login View
def login_view(request):
if request.method == 'POST':
form = AuthenticationForm(data=request.POST)
if form.is_valid():
user = form.get_user()
login(request, user)
return redirect('feed')
else:
form = AuthenticationForm()
return render(request, 'login.html', {'form': form})
# Logout View
@login_required
def logout_view(request):
logout(request)
return redirect('login')
# View for Logged In User Feed and Logged Out User Feed
def feed(request):
posts = Post.objects.all().order_by('-created_at')
if request.user.is_authenticated:
return render(request, 'feed.html', {'posts': posts})
else:
return render(request, 'guest_feed.html', {'posts': posts})
# View for Creating a Post
@login_required
def post_create(request):
if request.method == 'POST':
form = PostForm(request.POST)
if form.is_valid():
post = form.save(commit=False)
post.user = request.user
post.save()
return redirect('feed')
else:
form = PostForm()
return render(request, 'post_create.html', {'form': form})
# View for Deleting a Post
@login_required
def post_delete(request, pk):
post = get_object_or_404(Post, pk=pk, user=request.user)
post.delete()
return redirect('feed')
# View for Commenting on a Post
@login_required
def comment_create(request, pk):
post = get_object_or_404(Post, pk=pk)
if request.method == 'POST':
form = CommentForm(request.POST)
if form.is_valid():
comment = form.save(commit=False)
comment.user = request.user
comment.post = post
comment.save()
return redirect('feed')
else:
form = CommentForm()
return render(request, 'comment_create.html', {'form': form, 'post': post})
# View for Deleting a Comment
@login_required
def comment_delete(request, pk):
comment = get_object_or_404(Comment, pk=pk, user=request.user)
comment.delete()
return redirect('feed')
# View for Liking a Post
@login_required
def like(request, pk):
post = get_object_or_404(Post, pk=pk)
like_obj, created = Like.objects.get_or_create(user=request.user, post=post)
if not created:
like_obj.delete()
like_count = Like.objects.filter(post=post).count()
return redirect('feed')
# View for showing a user's profile
def user_profile(request, username):
user = get_object_or_404(User, username=username)
posts = Post.objects.filter(user=user)
likes = Like.objects.filter(user=user)
context = {
'profile_user': user,
'posts': posts,
'likes': likes,
}
return render(request, 'user_profile.html', context)
# View for showing a other user's profile for logged out users
def guest_profile(request, username):
user = get_object_or_404(User, username=username)
posts = Post.objects.filter(user=user)
return render(request, 'guest_profile.html', {'profile_user': user, 'posts': posts})
# View for changing a user's username
@login_required
def change_username(request):
if request.method == 'POST':
form = UsernameChangeForm(request.POST, instance=request.user)
if form.is_valid():
user = form.save()
update_session_auth_hash(request, user) # Update session to keep user logged in
messages.success(request, 'Your username has been updated.')
return redirect('user_profile', user.username)
else:
form = UsernameChangeForm(instance=request.user)
return render(request, 'change_username.html', {'form': form})
# View for searching users
def search_users(request):
query = request.GET.get('q', '')
users = User.objects.filter(
Q(username__icontains=query) |
Q(first_name__icontains=query) |
Q(last_name__icontains=query)
)
return render(request, 'search_users.html', {'users': users, 'query': query})
Writing the urls.py file in core app
Now for each view create a URL in "core/urls.py" file:
# core/urls.py
from django.urls import path, re_path
from . import views
from django.shortcuts import redirect
def redirect_to_feed(request):
return redirect('feed')
urlpatterns = [
# Redirecting to Home Page to Feed Page
path('', redirect_to_feed),
# Signup Page
path('signup/', views.signup, name='signup'),
# Login
path('login/', views.login_view, name='login'),
# Logout
path('logout/', views.logout_view, name='logout'),
# Feed Page
path('feed/', views.feed, name='feed'),
# Post Create
path('post/create/', views.post_create, name='post_create'),
# Post Delete
path('post/<int:pk>/delete/', views.post_delete, name='post_delete'),
# Comment on Posts
path('post/<int:pk>/comment/', views.comment_create, name='comment_create'),
# Delete Comment
path('comment/<int:pk>/delete/', views.comment_delete, name='comment_delete'),
# Like Post
path('post/<int:pk>/like/', views.like, name='like'),
# Redirecting to Latest Username
re_path(r'^profile/(?!change-username/)(?P<username>\w+)/$', views.user_profile, name='user_profile'),
# For fetching user the username on posts
path('guest/profile/<str:username>/', views.guest_profile, name='guest_profile'),
# Change Username
path('profile/change-username/', views.change_username, name='change_username'),
# Search Users
path('search/', views.search_users, name='search_users'),
]
Writing Models in models.py
Now other than Authentication system, users will interact with website's 4 features which require database interaction - Posting, Commenting, Like Posts and Changing Username so we will require 3 additional Models for Posting, Commenting and Like Posts, for changing the username, we will use Django's UsernameChangeForm form from built-in User Model which we are using for User Creation and Authentication.
# core/models.py
from django.db import models
from django.contrib.auth.models import User
class Post(models.Model):
content = models.TextField()
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='posts')
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return f"{self.user.username} - {self.content[:20]}..."
class Comment(models.Model):
content = models.TextField()
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='comments')
post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='comments')
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return f"{self.user.username} - {self.content[:20]}..."
class Like(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='likes')
post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='likes')
def __str__(self):
return f"{self.user.username} liked {self.post.content[:20]}..."
Writing forms.py file
Now other than authentication forms we are using three additional forms for Changing Username, Posting a Post and Comment form. All these three forms will require text fields and we already creating models for Post and Comment. For changing username, we will user Django's built-in User Model.
# core/forms.py
from django import forms
from .models import Post, Comment
from django.contrib.auth.models import User
class UsernameChangeForm(forms.ModelForm):
class Meta:
model = User
fields = ['username']
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = ['content']
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ['content']
Creating all required HTML Templates
We will require templates for main base page, signup page, login page, feed for logged in users, guest feed for logged out users, Posting page, Comment Page, User Profile, Guest Profile for Logged out users, Changing Username and Search Results Page. These are the template names we are using:
- base.html
- signup.html
- login.html
- feed.html
- guest_feed.html
- post_create.html
- comment_create.html
- user_profile.html
- guest_profile.html
- change_username.html
- search_users.html
Let's create a templates directory in core directory and then create each template:
core/templates/base.html
{% load bootstrap5 %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Social Media Feed</title>
{% bootstrap_css %}
{% load static %}
{% comment %}Add this Style page if you are doing any custom styling{% endcomment %}
{% comment %} <link rel="stylesheet" href="{% static 'css/styles.css' %}" /> {% endcomment %}
</head>
<body>
{% bootstrap_messages %}
<header>
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
<div class="container-fluid">
<a class="navbar-brand" href="{% url 'feed' %}">Social Media Feed</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation"><span class="navbar-toggler-icon"></span></button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
{% if user.is_authenticated %}
<li class="nav-item">
<a class="nav-link" href="{% url 'feed' %}">Feed</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{% url 'post_create' %}">New Post</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{% url 'logout' %}">Logout</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{% url 'user_profile' user.username %}">Profile</a>
</li>
{% else %}
<li class="nav-item">
<a class="nav-link" href="{% url 'login' %}">Login</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{% url 'signup' %}">Sign Up</a>
</li>
{% endif %}
</ul>
</div>
<form class="d-flex" role="search" action="{% url 'search_users' %}">
<input class="form-control me-2" type="search" placeholder="Search Users" aria-label="Search" name="q" />
<button class="btn btn-outline-light" type="submit">Search</button>
</form>
</div>
</nav>
</header>
<main class="container my-4">
{% block content %}
{% endblock %}
</main>
{% bootstrap_javascript %}
</body>
</html>
core/templates/signup.html
{% extends 'base.html' %}
{% load bootstrap5 %}
{% block content %}
<div class="row justify-content-center">
<div class="col-md-6">
<div class="card">
<div class="card-body">
<h2 class="card-title">Sign Up</h2>
<form method="post">
{% csrf_token %}
{% bootstrap_form form %}
<p>Already have an account? <a href="{% url 'login' %}">Login</a></p>
<button type="submit" class="btn btn-primary">Sign Up</button>
</form>
</div>
</div>
</div>
</div>
{% endblock %}
core/templates/login.html
{% extends 'base.html' %}
{% load bootstrap5 %}
{% block content %}
<div class="row justify-content-center">
<div class="col-md-6">
<div class="card">
<div class="card-body">
<h2 class="card-title">Login</h2>
<form method="post">
{% csrf_token %}
{% bootstrap_form form %}
<p>Don't have an account? <a href="{% url 'signup' %}">Signup</a></p>
<button type="submit" class="btn btn-primary">Login</button>
</form>
</div>
</div>
</div>
</div>
{% endblock %}
core/templates/feed.html
{% extends 'base.html' %}
{% block content %}
<h2>Feed</h2>
{% for post in posts %}
<div class="card mb-3">
<div class="card-body">
<p class="card-text">{{ post.content }}</p>
<p class="card-text">Posted by: {{ post.user.username }} on {{ post.created_at }}</p>
{% comment %} <p class="card-text">Likes: {{ post.likes.count }}</p> {% endcomment %}
<div class="d-flex justify-content-between">
<div>
<a href="{% url 'comment_create' post.pk %}" class="btn btn-primary btn-sm">Add Comment</a>
<a href="{% url 'like' post.pk %}" class="btn btn-secondary btn-sm">Like {{ post.likes.count }}</a>
</div>
{% if post.user == request.user %}
<a href="{% url 'post_delete' post.pk %}" class="btn btn-danger btn-sm">Delete</a>
{% endif %}
</div>
</div>
{% if post.comments.exists %}
<div class="card-footer">
<h5>Comments</h5>
{% for comment in post.comments.all %}
<div class="card mb-2">
<div class="card-body">
<p class="card-text">{{ comment.content }}</p>
<p class="card-text">Posted by: {{ comment.user.username }} on {{ comment.created_at }}</p>
{% if comment.user == request.user %}
<a href="{% url 'comment_delete' comment.pk %}" class="btn btn-danger btn-sm">Delete</a>
{% endif %}
</div>
</div>
{% endfor %}
</div>
{% endif %}
</div>
{% endfor %}
{% endblock %}
core/templates/guest_feed.html
{% extends 'base.html' %}
{% block content %}
<h2>Feed</h2>
{% for post in posts %}
<div class="card mb-3">
<div class="card-body">
<p class="card-text">{{ post.content }}</p>
<p class="card-text">
Posted by: <a href="{% url 'guest_profile' post.user.username %}">{{ post.user.username }}</a> on {{ post.created_at }}
</p>
<p class="card-text">Likes: {{ post.likes.count }}</p>
</div>
{% if post.comments.exists %}
<div class="card-footer">
<h5>Comments</h5>
{% for comment in post.comments.all %}
<div class="card mb-2">
<div class="card-body">
<p class="card-text">{{ comment.content }}</p>
<p class="card-text">Posted by: {{ comment.user.username }} on {{ comment.created_at }}</p>
{% if comment.user == request.user %}
<a href="{% url 'comment_delete' comment.pk %}" class="btn btn-danger btn-sm">Delete</a>
{% endif %}
</div>
</div>
{% endfor %}
</div>
{% endif %}
</div>
{% endfor %}
<p>
Please <a href="{% url 'login' %}">login</a> or <a href="{% url 'signup' %}">signup</a> to interact with posts.
</p>
{% endblock %}
core/templates/post_create.html
{% extends 'base.html' %}
{% load bootstrap5 %}
{% block content %}
<div class="row justify-content-center">
<div class="col-md-6">
<div class="card">
<div class="card-body">
<h2 class="card-title">New Post</h2>
<form method="post">
{% csrf_token %}
{% bootstrap_form form %}
<button type="submit" class="btn btn-primary">Post</button>
</form>
</div>
</div>
</div>
</div>
{% endblock %}
core/templates/comment_create.html
{% extends 'base.html' %}
{% load bootstrap5 %}
{% block content %}
<div class="row justify-content-center">
<div class="col-md-6">
<div class="card">
<div class="card-body">
<h2 class="card-title">Add Comment</h2>
<p class="card-text">{{ post.content }}</p>
<form method="post">
{% csrf_token %}
{% bootstrap_form form %}
<button type="submit" class="btn btn-primary">Comment</button>
</form>
</div>
</div>
</div>
</div>
{% endblock %}
core/templates/user_profile.html
{% extends 'base.html' %}
{% block content %}
<div class="row">
<div class="col-md-6">
<h2>{{ profile_user.username }}'s Profile</h2>
{% if request.user == profile_user %}
<p>
<a href="{% url 'change_username' %}">Change Username</a>
</p>
{% endif %}
<h4>Posts</h4>
{% for post in posts %}
<div class="card mb-3">
<div class="card-body">
<p class="card-text">{{ post.content }}</p>
<p class="card-text">Posted on {{ post.created_at }}</p>
</div>
</div>
{% empty %}
<p>No posts yet.</p>
{% endfor %}
</div>
<div class="col-md-6">
<h4>Liked Posts</h4>
{% for like in likes %}
<div class="card mb-3">
<div class="card-body">
<p class="card-text">{{ like.post.content }}</p>
<p class="card-text">Posted by: {{ like.post.user.username }} on {{ like.post.created_at }}</p>
</div>
</div>
{% empty %}
<p>No liked posts yet.</p>
{% endfor %}
</div>
</div>
{% endblock %}
core/templates/guest_profile.html
{% extends 'base.html' %}
{% block content %}
<h2>{{ profile_user.username }}'s Profile</h2>
<p>Please <a href="{% url 'login' %}">log in</a> to view this user's profile.</p>
{% endblock %}
core/templates/change_username.html
{% extends 'base.html' %}
{% load bootstrap5 %}
{% block content %}
<div class="row justify-content-center">
<div class="col-md-6">
<div class="card">
<div class="card-body">
<h2 class="card-title">Change Username</h2>
<form method="post">
{% csrf_token %}
{% bootstrap_form form %}
<button type="submit" class="btn btn-primary">Change Username</button>
</form>
</div>
</div>
</div>
</div>
{% endblock %}
core/templates/search_users.html
{% extends 'base.html' %}
{% block content %}
<h2>Search Results for "{{ query }}"</h2>
{% if users %}
<ul>
{% for user in users %}
<li>
<a href="{% url 'user_profile' user.username %}">{{ user.username }}</a>
</li>
{% endfor %}
</ul>
{% else %}
<p>No users found.</p>
{% endif %}
{% endblock %}
Till now, we creates all required views, models, urls and templates now we have to register created models in admin.py
Wrting the admin.py file
Django provides a default admin panel and to see the data in admin panel we need to register all the models we created in admin.py.
core/admin.py:
# core/admin.py
from django.contrib import admin
from .models import Post, Comment, Like
# Register your models here.
admin.site.register(Post)
admin.site.register(Comment)
admin.site.register(Like)
We did not registered Authentication models in admin.py because we used Django's built-in User Models so they will directly be available in admin panel. Now, the creation of our Social Media Feed using Django is done.
Making Migrations and Creating Super User
We have to make the migrations and migrate all models and views into database, we are using sqlite3 database which is provided by default in Django.
python manage.py makemigrations
python manage.py migrate

Creating Super User
python manage.py createsuperuser
Testing our Social Media Feed
Deploy the project using the following command:
python manage.py runserver
Video Demonstration:
You can download the full source code from my GitHub Repo: https://github.com/shravanngoswamii/social-media-feed-django