Project: Social Media Platform with Django Channels

Building complete social media platform demonstrates Django advanced features implementing user profiles, posts, likes, comments, real-time notifications, and messaging with Django Channels maintaining production-ready architecture for interactive web applications. Social media platforms require complex functionality including user relationships (followers/following), activity feeds, real-time updates through WebSockets, content moderation, and notification systems creating sophisticated requirements beyond traditional request-response patterns. Traditional Django handles HTTP requests synchronously while WebSocket connections maintain persistent bidirectional communication enabling instant updates when users receive messages, notifications, or see new posts appearing in feeds. Without real-time features, users must refresh pages manually to see updates creating poor user experience compared to modern applications providing instant feedback. This comprehensive tutorial builds fully-functional social platform from scratch including project setup with Channels and Redis, user profile system with avatars and bios, post creation with images and captions, like and comment functionality, follower/following relationships, activity feed showing followed users' posts, real-time notifications using WebSockets, direct messaging between users, notification system for likes and comments, search functionality finding users and posts, and admin interface managing content moderation. Real-world features include privacy settings controlling post visibility, blocking users preventing unwanted interactions, reporting inappropriate content, hashtag support for discovery, mention system tagging users in posts, and content recommendations. Project demonstrates Django 6.0 best practices including models with proper relationships, WebSocket consumers handling real-time events, channel layers broadcasting messages, class-based views maintaining code reusability, security measures protecting user data, and scalable architecture supporting thousands of concurrent connections. This hands-on guide provides complete code building professional social media platform from initial setup through real-time features and production deployment teaching practical Django development maintaining performance throughout.
Project Setup with Channels
Project setup creates Django environment installing Channels for WebSocket support, Redis for channel layer, and configuring ASGI for asynchronous handling. Understanding Channels architecture integrated with Django project structure enables building real-time applications maintaining scalability.
# Django 6.0 Social Media Project Setup
# Create virtual environment
python -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
# Install dependencies
pip install Django==6.0
pip install channels==4.0.0
pip install channels-redis
pip install Pillow # Image handling
pip install django-crispy-forms crispy-bootstrap5
# Install Redis (required for channel layer)
# Ubuntu/Debian: sudo apt-get install redis-server
# macOS: brew install redis
# Start Redis: redis-server
# Create project
django-admin startproject social_project
cd social_project
# Create apps
python manage.py startapp accounts
python manage.py startapp posts
python manage.py startapp notifications
python manage.py startapp chat
# Project structure:
# social_project/
# βββ manage.py
# βββ social_project/
# β βββ asgi.py # ASGI configuration
# β βββ settings.py
# β βββ urls.py
# β βββ routing.py # WebSocket routing
# βββ accounts/ # User profiles
# βββ posts/ # Posts, likes, comments
# βββ notifications/ # Real-time notifications
# βββ chat/ # Direct messaging
# Configure settings.py
INSTALLED_APPS = [
'daphne', # Must be first for Channels
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
# Third-party
'channels',
'crispy_forms',
'crispy_bootstrap5',
# Local apps
'accounts',
'posts',
'notifications',
'chat',
]
# ASGI application
ASGI_APPLICATION = 'social_project.asgi.application'
# Channel layers configuration
CHANNEL_LAYERS = {
'default': {
'BACKEND': 'channels_redis.core.RedisChannelLayer',
'CONFIG': {
'hosts': [('127.0.0.1', 6379)],
},
},
}
# Media files
import os
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
# ASGI configuration
# social_project/asgi.py
import os
from django.core.asgi import get_asgi_application
from channels.routing import ProtocolTypeRouter, URLRouter
from channels.auth import AuthMiddlewareStack
import social_project.routing
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'social_project.settings')
application = ProtocolTypeRouter({
'http': get_asgi_application(),
'websocket': AuthMiddlewareStack(
URLRouter(
social_project.routing.websocket_urlpatterns
)
),
})
# WebSocket routing
# social_project/routing.py
from django.urls import path
from notifications import consumers as notification_consumers
from chat import consumers as chat_consumers
websocket_urlpatterns = [
path('ws/notifications/', notification_consumers.NotificationConsumer.as_asgi()),
path('ws/chat/<int:user_id>/', chat_consumers.ChatConsumer.as_asgi()),
]User Profiles and Relationships
User models extend Django's User with Profile storing additional information and Follow model tracking follower/following relationships with symmetrical connections. Understanding user relationships integrated with Django relationships enables building social graphs maintaining data integrity.
# User profile and relationship models
# accounts/models.py
from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
bio = models.TextField(max_length=500, blank=True)
avatar = models.ImageField(upload_to='avatars/', blank=True, null=True)
location = models.CharField(max_length=100, blank=True)
website = models.URLField(blank=True)
birth_date = models.DateField(null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return f'{self.user.username} Profile'
@property
def followers_count(self):
return self.user.followers.count()
@property
def following_count(self):
return self.user.following.count()
# Auto-create profile for new users
@receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
@receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
instance.profile.save()
class Follow(models.Model):
follower = models.ForeignKey(User, on_delete=models.CASCADE, related_name='following')
following = models.ForeignKey(User, on_delete=models.CASCADE, related_name='followers')
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
unique_together = ['follower', 'following']
ordering = ['-created_at']
def __str__(self):
return f'{self.follower.username} follows {self.following.username}'
# Post models
# posts/models.py
from django.db import models
from django.contrib.auth.models import User
class Post(models.Model):
author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='posts')
content = models.TextField(max_length=500)
image = models.ImageField(upload_to='posts/', blank=True, null=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
ordering = ['-created_at']
def __str__(self):
return f'Post by {self.author.username}'
@property
def likes_count(self):
return self.likes.count()
@property
def comments_count(self):
return self.comments.count()
class Like(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='likes')
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
unique_together = ['user', 'post']
def __str__(self):
return f'{self.user.username} likes {self.post.id}'
class Comment(models.Model):
post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='comments')
author = models.ForeignKey(User, on_delete=models.CASCADE)
content = models.TextField(max_length=300)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ['created_at']
def __str__(self):
return f'Comment by {self.author.username}'
# Run migrations
python manage.py makemigrations
python manage.py migrateViews and Activity Feed
Views implement social features with activity feed showing followed users' posts, profile pages displaying user information and posts, follow/unfollow functionality managing relationships, and post CRUD operations. Understanding view implementation enables building complete social interactions maintaining user engagement.
# Social media views
# posts/views.py
from django.shortcuts import render, redirect, get_object_or_404
from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User
from django.db.models import Q
from .models import Post, Like, Comment
from .forms import PostForm, CommentForm
from accounts.models import Follow
@login_required
def feed(request):
# Get posts from followed users and own posts
following_users = request.user.following.values_list('following', flat=True)
posts = Post.objects.filter(
Q(author__in=following_users) | Q(author=request.user)
).select_related('author').prefetch_related('likes', 'comments')
if request.method == 'POST':
form = PostForm(request.POST, request.FILES)
if form.is_valid():
post = form.save(commit=False)
post.author = request.user
post.save()
return redirect('posts:feed')
else:
form = PostForm()
return render(request, 'posts/feed.html', {
'posts': posts,
'form': form
})
@login_required
def like_post(request, post_id):
post = get_object_or_404(Post, id=post_id)
like, created = Like.objects.get_or_create(
user=request.user,
post=post
)
if not created:
like.delete()
else:
# Send notification
from notifications.models import Notification
if post.author != request.user:
Notification.objects.create(
recipient=post.author,
sender=request.user,
notification_type='like',
post=post
)
return redirect(request.META.get('HTTP_REFERER', 'posts:feed'))
@login_required
def add_comment(request, post_id):
post = get_object_or_404(Post, id=post_id)
if request.method == 'POST':
form = CommentForm(request.POST)
if form.is_valid():
comment = form.save(commit=False)
comment.post = post
comment.author = request.user
comment.save()
# Send notification
from notifications.models import Notification
if post.author != request.user:
Notification.objects.create(
recipient=post.author,
sender=request.user,
notification_type='comment',
post=post,
comment=comment
)
return redirect(request.META.get('HTTP_REFERER', 'posts:feed'))
# User profile views
# accounts/views.py
from django.shortcuts import render, redirect, get_object_or_404
from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User
from .models import Follow
from posts.models import Post
@login_required
def profile(request, username):
user = get_object_or_404(User, username=username)
posts = Post.objects.filter(author=user)
is_following = Follow.objects.filter(
follower=request.user,
following=user
).exists()
return render(request, 'accounts/profile.html', {
'profile_user': user,
'posts': posts,
'is_following': is_following
})
@login_required
def follow_user(request, user_id):
user_to_follow = get_object_or_404(User, id=user_id)
if user_to_follow != request.user:
follow, created = Follow.objects.get_or_create(
follower=request.user,
following=user_to_follow
)
if not created:
follow.delete()
else:
# Send notification
from notifications.models import Notification
Notification.objects.create(
recipient=user_to_follow,
sender=request.user,
notification_type='follow'
)
return redirect('accounts:profile', username=user_to_follow.username)
@login_required
def search(request):
query = request.GET.get('q', '')
if query:
users = User.objects.filter(
Q(username__icontains=query) |
Q(first_name__icontains=query) |
Q(last_name__icontains=query)
)[:20]
posts = Post.objects.filter(
content__icontains=query
)[:20]
else:
users = []
posts = []
return render(request, 'search.html', {
'users': users,
'posts': posts,
'query': query
})Real-Time Notifications with WebSockets
WebSocket consumers handle real-time notifications sending instant updates when users receive likes, comments, or new followers without page refresh. Understanding WebSocket implementation enables building responsive applications maintaining live connections with users.
# Notification model
# notifications/models.py
from django.db import models
from django.contrib.auth.models import User
from posts.models import Post, Comment
class Notification(models.Model):
NOTIFICATION_TYPES = [
('like', 'Like'),
('comment', 'Comment'),
('follow', 'Follow'),
('mention', 'Mention'),
]
recipient = models.ForeignKey(User, on_delete=models.CASCADE, related_name='notifications')
sender = models.ForeignKey(User, on_delete=models.CASCADE, related_name='sent_notifications')
notification_type = models.CharField(max_length=20, choices=NOTIFICATION_TYPES)
post = models.ForeignKey(Post, on_delete=models.CASCADE, null=True, blank=True)
comment = models.ForeignKey(Comment, on_delete=models.CASCADE, null=True, blank=True)
read = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ['-created_at']
def __str__(self):
return f'{self.notification_type} from {self.sender.username}'
# WebSocket consumer for notifications
# notifications/consumers.py
import json
from channels.generic.websocket import AsyncWebsocketConsumer
from channels.db import database_sync_to_async
from django.contrib.auth.models import User
class NotificationConsumer(AsyncWebsocketConsumer):
async def connect(self):
self.user = self.scope['user']
if self.user.is_authenticated:
self.group_name = f'notifications_{self.user.id}'
# Join notification group
await self.channel_layer.group_add(
self.group_name,
self.channel_name
)
await self.accept()
# Send unread count on connect
unread_count = await self.get_unread_count()
await self.send(text_data=json.dumps({
'type': 'unread_count',
'count': unread_count
}))
else:
await self.close()
async def disconnect(self, close_code):
if hasattr(self, 'group_name'):
await self.channel_layer.group_discard(
self.group_name,
self.channel_name
)
async def receive(self, text_data):
data = json.loads(text_data)
if data.get('action') == 'mark_read':
notification_id = data.get('notification_id')
await self.mark_notification_read(notification_id)
async def notification_message(self, event):
await self.send(text_data=json.dumps({
'type': 'notification',
'notification': event['notification']
}))
@database_sync_to_async
def get_unread_count(self):
from .models import Notification
return Notification.objects.filter(
recipient=self.user,
read=False
).count()
@database_sync_to_async
def mark_notification_read(self, notification_id):
from .models import Notification
Notification.objects.filter(
id=notification_id,
recipient=self.user
).update(read=True)
# Send notification helper
# notifications/utils.py
from channels.layers import get_channel_layer
from asgiref.sync import async_to_sync
def send_notification(recipient, notification_data):
channel_layer = get_channel_layer()
group_name = f'notifications_{recipient.id}'
async_to_sync(channel_layer.group_send)(
group_name,
{
'type': 'notification_message',
'notification': notification_data
}
)
# Frontend WebSocket connection
# templates/base.html
<script>
const notificationSocket = new WebSocket(
'ws://' + window.location.host + '/ws/notifications/'
);
notificationSocket.onmessage = function(e) {
const data = JSON.parse(e.data);
if (data.type === 'notification') {
// Show notification
showNotification(data.notification);
updateNotificationBadge();
} else if (data.type === 'unread_count') {
updateNotificationBadge(data.count);
}
};
function showNotification(notification) {
// Display notification in UI
const notificationHtml = `
<div class="notification">
<strong>${notification.sender}</strong> ${notification.message}
</div>
`;
document.getElementById('notifications-list').innerHTML += notificationHtml;
}
function updateNotificationBadge(count) {
const badge = document.getElementById('notification-badge');
if (count > 0) {
badge.textContent = count;
badge.style.display = 'inline';
} else {
badge.style.display = 'none';
}
}
</script>Direct Messaging System
Direct messaging enables users sending private messages through WebSocket connections maintaining chat history with database persistence. Understanding chat implementation enables building real-time communication features maintaining message delivery and read receipts.
# Chat models
# chat/models.py
from django.db import models
from django.contrib.auth.models import User
class Message(models.Model):
sender = models.ForeignKey(User, on_delete=models.CASCADE, related_name='sent_messages')
recipient = models.ForeignKey(User, on_delete=models.CASCADE, related_name='received_messages')
content = models.TextField()
read = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ['created_at']
def __str__(self):
return f'Message from {self.sender.username} to {self.recipient.username}'
# Chat consumer
# chat/consumers.py
import json
from channels.generic.websocket import AsyncWebsocketConsumer
from channels.db import database_sync_to_async
from django.contrib.auth.models import User
class ChatConsumer(AsyncWebsocketConsumer):
async def connect(self):
self.user = self.scope['user']
self.other_user_id = self.scope['url_route']['kwargs']['user_id']
# Create unique room name
users = sorted([self.user.id, int(self.other_user_id)])
self.room_name = f'chat_{users[0]}_{users[1]}'
await self.channel_layer.group_add(
self.room_name,
self.channel_name
)
await self.accept()
# Load chat history
messages = await self.get_messages()
await self.send(text_data=json.dumps({
'type': 'chat_history',
'messages': messages
}))
async def disconnect(self, close_code):
await self.channel_layer.group_discard(
self.room_name,
self.channel_name
)
async def receive(self, text_data):
data = json.loads(text_data)
message_content = data['message']
# Save message to database
message = await self.save_message(message_content)
# Broadcast to room
await self.channel_layer.group_send(
self.room_name,
{
'type': 'chat_message',
'message': {
'id': message.id,
'sender': self.user.username,
'content': message_content,
'created_at': message.created_at.isoformat()
}
}
)
async def chat_message(self, event):
await self.send(text_data=json.dumps({
'type': 'message',
'message': event['message']
}))
@database_sync_to_async
def save_message(self, content):
from .models import Message
other_user = User.objects.get(id=self.other_user_id)
return Message.objects.create(
sender=self.user,
recipient=other_user,
content=content
)
@database_sync_to_async
def get_messages(self):
from .models import Message
other_user = User.objects.get(id=self.other_user_id)
messages = Message.objects.filter(
models.Q(sender=self.user, recipient=other_user) |
models.Q(sender=other_user, recipient=self.user)
).order_by('created_at')[:50]
return [{
'id': msg.id,
'sender': msg.sender.username,
'content': msg.content,
'created_at': msg.created_at.isoformat()
} for msg in messages]
# Chat view
# chat/views.py
from django.shortcuts import render, get_object_or_404
from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User
@login_required
def chat_room(request, user_id):
other_user = get_object_or_404(User, id=user_id)
return render(request, 'chat/room.html', {
'other_user': other_user
})| Feature | Technology | Purpose | User Benefit |
|---|---|---|---|
| User Profiles | OneToOne relationship | Store user info | Personalized profiles |
| Follow System | Many-to-Many through model | User relationships | Social connections |
| Activity Feed | Filtered QuerySets | Show relevant posts | Engaging content |
| Real-time Notifications | Django Channels + WebSockets | Instant updates | Live interaction |
| Direct Messaging | WebSocket consumers | Private communication | User engagement |
Social Platform Best Practices
- Implement privacy settings: Allow users controlling post visibility and profile access maintaining privacy
- Use select_related/prefetch_related: Optimize database queries preventing N+1 problems maintaining performance
- Add content moderation: Implement reporting and blocking features preventing abuse maintaining community safety
- Optimize WebSocket connections: Use channel layers efficiently handling concurrent connections at scale
- Implement pagination: Add infinite scroll or pagination preventing loading excessive data maintaining speed
- Cache user feeds: Use Redis caching for frequently accessed feeds reducing database load
- Handle connection failures: Implement reconnection logic maintaining reliability when WebSocket drops
- Add rate limiting: Prevent spam with rate limits on posts, messages, and follows maintaining quality
- Monitor performance: Track WebSocket connections, message throughput, and database queries optimizing bottlenecks
- Test real-time features: Write tests for WebSocket consumers ensuring reliability
Conclusion
Building complete social media platform demonstrates Django Channels for real-time features implementing WebSocket connections maintaining persistent bidirectional communication enabling instant updates when users receive notifications, messages, or see new posts appearing in feeds without refresh. Project setup installs Channels for WebSocket support with Redis providing channel layer enabling message broadcasting across server instances maintaining scalability. User models extend Django's User with Profile storing additional information and Follow model tracking follower/following relationships with unique_together constraint preventing duplicate follows maintaining data integrity. Activity feed queries posts from followed users using QuerySet filtering with select_related optimizing database access while views handle post creation, likes, comments, and follow functionality integrating business logic maintaining clean separation. Real-time notifications use WebSocket consumers connecting users to notification groups sending instant updates when receiving likes, comments, or new followers with channel layer broadcasting messages across connections maintaining live updates. Direct messaging implements chat functionality with Message model persisting chat history and ChatConsumer handling WebSocket connections creating unique room names sorting user IDs maintaining bidirectional communication with message broadcasting to room members. Features include user profiles with avatars and bios, post creation with images, like and comment functionality with notifications, follower/following relationships, activity feed showing relevant posts, real-time notifications through WebSockets, direct messaging between users, search finding users and posts, and content moderation with reporting. Best practices include implementing privacy settings controlling visibility, using select_related preventing N+1 queries, adding content moderation with reporting and blocking, optimizing WebSocket connections handling scale, implementing pagination preventing excessive loading, caching user feeds reducing database load, handling connection failures with reconnection logic, adding rate limiting preventing spam, monitoring performance tracking metrics, and testing real-time features ensuring reliability. Understanding complete social platform development from user relationships through real-time messaging integrated with Django Channels, WebSockets, Redis, and database optimization provides practical foundation building production-ready social applications serving thousands of concurrent users maintaining performance and reliability throughout application lifecycle.
$ share --platform
$ cat /comments/ (0)
$ cat /comments/
// No comments found. Be the first!


