每天40分玩转Django:Django实战 - 社交网络开发
Django实战 - 社交网络开发
一、功能模块概览表
模块 | 主要功能 | 技术要点 | 难度 |
---|---|---|---|
用户资料 | 个人信息管理、头像上传 | 文件处理、表单验证 | ★★★☆☆ |
好友关系 | 好友添加、删除、关注 | 多对多关系、信号 | ★★★★☆ |
消息系统 | 私信、通知、实时聊天 | WebSocket、异步处理 | ★★★★★ |
二、详细代码实现
2.1 用户资料模块
# 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)
avatar = models.ImageField(upload_to='avatars/', default='avatars/default.png')
bio = models.TextField(max_length=500, blank=True)
location = models.CharField(max_length=100, blank=True)
birth_date = models.DateField(null=True, blank=True)
interests = models.CharField(max_length=200, blank=True)
def __str__(self):
return f'{self.user.username}的个人资料'
@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()
# forms.py
from django import forms
from .models import Profile
class ProfileForm(forms.ModelForm):
class Meta:
model = Profile
fields = ['avatar', 'bio', 'location', 'birth_date', 'interests']
widgets = {
'birth_date': forms.DateInput(attrs={'type': 'date'}),
'bio': forms.Textarea(attrs={'rows': 4}),
}
# views.py
from django.shortcuts import render, redirect
from django.contrib.auth.decorators import login_required
from django.contrib import messages
from .forms import ProfileForm
@login_required
def profile_edit(request):
if request.method == 'POST':
form = ProfileForm(request.POST, request.FILES, instance=request.user.profile)
if form.is_valid():
form.save()
messages.success(request, '个人资料更新成功!')
return redirect('profile_view')
else:
form = ProfileForm(instance=request.user.profile)
return render(request, 'profiles/edit.html', {'form': form})
2.2 好友关系模块
# models.py
class Friendship(models.Model):
FRIEND_STATUS = (
('pending', '待确认'),
('accepted', '已接受'),
('declined', '已拒绝'),
)
sender = models.ForeignKey(User, related_name='friendship_requests_sent',
on_delete=models.CASCADE)
receiver = models.ForeignKey(User, related_name='friendship_requests_received',
on_delete=models.CASCADE)
status = models.CharField(max_length=10, choices=FRIEND_STATUS, default='pending')
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
unique_together = ('sender', 'receiver')
# views.py
from django.shortcuts import get_object_or_404
from .models import Friendship
@login_required
def send_friend_request(request, user_id):
receiver = get_object_or_404(User, id=user_id)
if request.user != receiver:
Friendship.objects.get_or_create(
sender=request.user,
receiver=receiver,
defaults={'status': 'pending'}
)
messages.success(request, '好友请求已发送!')
return redirect('profile_view', user_id=user_id)
@login_required
def accept_friend_request(request, friendship_id):
friendship = get_object_or_404(Friendship, id=friendship_id,
receiver=request.user,
status='pending')
friendship.status = 'accepted'
friendship.save()
messages.success(request, '已接受好友请求!')
return redirect('friend_requests')
2.3 消息系统模块
# models.py
class Message(models.Model):
sender = models.ForeignKey(User, related_name='sent_messages',
on_delete=models.CASCADE)
receiver = models.ForeignKey(User, related_name='received_messages',
on_delete=models.CASCADE)
content = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
is_read = models.BooleanField(default=False)
class Meta:
ordering = ['-created_at']
class Notification(models.Model):
NOTIFICATION_TYPES = (
('friend_request', '好友请求'),
('message', '新消息'),
('system', '系统通知'),
)
user = models.ForeignKey(User, on_delete=models.CASCADE)
notification_type = models.CharField(max_length=20, choices=NOTIFICATION_TYPES)
content = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
is_read = models.BooleanField(default=False)
# consumers.py
from channels.generic.websocket import AsyncWebsocketConsumer
import json
class ChatConsumer(AsyncWebsocketConsumer):
async def connect(self):
self.room_name = self.scope['url_route']['kwargs']['room_name']
self.room_group_name = f'chat_{self.room_name}'
await self.channel_layer.group_add(
self.room_group_name,
self.channel_name
)
await self.accept()
async def disconnect(self, close_code):
await self.channel_layer.group_discard(
self.room_group_name,
self.channel_name
)
async def receive(self, text_data):
text_data_json = json.loads(text_data)
message = text_data_json['message']
await self.channel_layer.group_send(
self.room_group_name,
{
'type': 'chat_message',
'message': message
}
)
async def chat_message(self, event):
message = event['message']
await self.send(text_data=json.dumps({
'message': message
}))
三、URL配置
# urls.py
from django.urls import path
from . import views
urlpatterns = [
# 用户资料相关
path('profile/edit/', views.profile_edit, name='profile_edit'),
path('profile/<int:user_id>/', views.profile_view, name='profile_view'),
# 好友关系相关
path('friend/request/<int:user_id>/', views.send_friend_request,
name='send_friend_request'),
path('friend/accept/<int:friendship_id>/', views.accept_friend_request,
name='accept_friend_request'),
path('friend/decline/<int:friendship_id>/', views.decline_friend_request,
name='decline_friend_request'),
# 消息系统相关
path('messages/', views.message_list, name='message_list'),
path('messages/send/<int:user_id>/', views.send_message, name='send_message'),
path('notifications/', views.notification_list, name='notification_list'),
]
四、实现流程图
五、前端模板实现
<!-- templates/profiles/edit.html -->
{% extends 'base.html' %}
{% block content %}
<div class="container mt-4">
<h2>编辑个人资料</h2>
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
<div class="form-group">
<label>当前头像</label>
{% if user.profile.avatar %}
<img src="{{ user.profile.avatar.url }}" class="avatar-preview">
{% endif %}
{{ form.avatar }}
</div>
{{ form.as_p }}
<button type="submit" class="btn btn-primary">保存更改</button>
</form>
</div>
{% endblock %}
<!-- templates/messages/chat.html -->
{% extends 'base.html' %}
{% block content %}
<div class="chat-container">
<div class="chat-messages" id="chat-messages">
{% for message in messages %}
<div class="message {% if message.sender == request.user %}sent{% else %}received{% endif %}">
<div class="message-content">{{ message.content }}</div>
<div class="message-time">{{ message.created_at|date:"H:i" }}</div>
</div>
{% endfor %}
</div>
<div class="chat-input">
<form id="chat-form">
<input type="text" id="chat-message-input" class="form-control">
<button type="submit" class="btn btn-primary">发送</button>
</form>
</div>
</div>
{% block extra_js %}
<script>
const chatSocket = new WebSocket(
'ws://' + window.location.host +
'/ws/chat/{{ room_name }}/'
);
chatSocket.onmessage = function(e) {
const data = JSON.parse(e.data);
const messages = document.querySelector('#chat-messages');
messages.innerHTML += `
<div class="message received">
<div class="message-content">${data.message}</div>
<div class="message-time">${new Date().toLocaleTimeString()}</div>
</div>
`;
messages.scrollTop = messages.scrollHeight;
};
document.querySelector('#chat-form').onsubmit = function(e) {
e.preventDefault();
const messageInput = document.querySelector('#chat-message-input');
const message = messageInput.value;
chatSocket.send(JSON.stringify({
'message': message
}));
messageInput.value = '';
};
</script>
{% endblock %}
{% endblock %}
六、测试用例
# tests.py
from django.test import TestCase, Client
from django.contrib.auth.models import User
from .models import Profile, Friendship, Message
class SocialNetworkTests(TestCase):
def setUp(self):
self.client = Client()
self.user1 = User.objects.create_user('user1', 'user1@test.com', 'password123')
self.user2 = User.objects.create_user('user2', 'user2@test.com', 'password123')
def test_profile_creation(self):
"""测试用户注册时是否自动创建个人资料"""
self.assertTrue(hasattr(self.user1, 'profile'))
self.assertIsInstance(self.user1.profile, Profile)
def test_friend_request(self):
"""测试好友请求功能"""
self.client.login(username='user1', password='password123')
response = self.client.post(f'/friend/request/{self.user2.id}/')
self.assertEqual(response.status_code, 302)
friendship = Friendship.objects.get(sender=self.user1, receiver=self.user2)
self.assertEqual(friendship.status, 'pending')
def test_message_system(self):
"""测试消息系统"""
message = Message.objects.create(
sender=self.user1,
receiver=self.user2,
content='Test message'
)
self.assertFalse(message.is_read)
七、部署注意事项
- WebSocket配置
# settings.py
INSTALLED_APPS += ['channels']
ASGI_APPLICATION = 'myproject.asgi.application'
CHANNEL_LAYERS = {
'default': {
'BACKEND': 'channels_redis.core.RedisChannelLayer',
'CONFIG': {
"hosts": [('127.0.0.1', 6379)],
},
},
}
- 静态文件处理
# settings.py
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
# 开发环境下的媒体文件服务
if DEBUG:
urlpatterns += static(MEDIA_URL, document_root=MEDIA_ROOT)
八、总结与扩展建议
-
性能优化方向:
- 使用缓存减少数据库查询
- 消息队列处理异步任务
- WebSocket连接池管理
-
功能扩展建议:
- 添加群聊功能
- 实现消息撤回
- 添加表情包支持
- 集成第三方登录
-
安全性考虑:
- 消息加密传输
- 限制文件上传类型和大小
- 防止CSRF攻击
- XSS防护
本次介绍了如何使用Django框架构建一个基础的社交网络系统,包括用户资料、好友关系和即时通讯等核心功能。建议在理解基础代码的同时,尝试添加更多功能,提高系统的可用性和安全性。
怎么样今天的内容还满意吗?再次感谢朋友们的观看,关注GZH:凡人的AI工具箱,回复666,送您价值199的AI大礼包。最后,祝您早日实现财务自由,还请给个赞,谢谢!