每天40分玩转Django:Django实战 - 社交网络开发

news/2025/1/7 12:37:02/

Django实战 - 社交网络开发

一、功能模块概览表

模块主要功能技术要点难度
用户资料个人信息管理、头像上传文件处理、表单验证★★★☆☆
好友关系好友添加、删除、关注多对多关系、信号★★★★☆
消息系统私信、通知、实时聊天WebSocket、异步处理★★★★★

二、详细代码实现

2.1 用户资料模块

python"># 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 receiverclass 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()
python"># forms.py
from django import forms
from .models import Profileclass ProfileForm(forms.ModelForm):class Meta:model = Profilefields = ['avatar', 'bio', 'location', 'birth_date', 'interests']widgets = {'birth_date': forms.DateInput(attrs={'type': 'date'}),'bio': forms.Textarea(attrs={'rows': 4}),}
python"># 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 好友关系模块

python"># 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')
python"># 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 消息系统模块

python"># 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)
python"># consumers.py
from channels.generic.websocket import AsyncWebsocketConsumer
import jsonclass 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配置

python"># urls.py
from django.urls import path
from . import viewsurlpatterns = [# 用户资料相关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 %}

六、测试用例

python"># tests.py
from django.test import TestCase, Client
from django.contrib.auth.models import User
from .models import Profile, Friendship, Messageclass 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)

七、部署注意事项

  1. WebSocket配置
python"># 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)],},},
}
  1. 静态文件处理
python"># settings.py
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')# 开发环境下的媒体文件服务
if DEBUG:urlpatterns += static(MEDIA_URL, document_root=MEDIA_ROOT)

八、总结与扩展建议

  1. 性能优化方向:

    • 使用缓存减少数据库查询
    • 消息队列处理异步任务
    • WebSocket连接池管理
  2. 功能扩展建议:

    • 添加群聊功能
    • 实现消息撤回
    • 添加表情包支持
    • 集成第三方登录
  3. 安全性考虑:

    • 消息加密传输
    • 限制文件上传类型和大小
    • 防止CSRF攻击
    • XSS防护

本次介绍了如何使用Django框架构建一个基础的社交网络系统,包括用户资料、好友关系和即时通讯等核心功能。建议在理解基础代码的同时,尝试添加更多功能,提高系统的可用性和安全性。


怎么样今天的内容还满意吗?再次感谢朋友们的观看,关注GZH:凡人的AI工具箱,回复666,送您价值199的AI大礼包。最后,祝您早日实现财务自由,还请给个赞,谢谢!


http://www.ppmy.cn/news/1560843.html

相关文章

element ui Switch组件二次修改

在element ui 中&#xff0c;开关的文字不能够放到开关里面&#xff0c;只能放到外面&#xff0c;这样不仅看起来很难看&#xff0c;也非常占用地方 想实现下方一样的样式&#xff0c;把文字放到开关里面&#xff0c;可惜element ui并不支持这个功能&#xff0c;只有element pl…

宽带、光猫、路由器、WiFi、光纤之间的关系

1、宽带&#xff08;Broadband&#xff09; 1.1 宽带的定义宽带指的是一种高速互联网接入技术&#xff0c;通常包括ADSL、光纤、4G/5G等不同类型的接入方式。宽带的关键特点是能够提供较高的数据传输速率&#xff0c;使得用户可以享受到稳定的上网体验。 1.2 宽带的作用宽带是…

江遇.BGP路由属性

BGP路由属性 一组描述BGP路由特性的参数&#xff0c;在配置路由策略时被广泛使用。对于企业 和运营商所关心的问题&#xff0c;如&#xff1a;如何过滤某些BGP路由&#xff1f;如何影响BGP的选路&#xff1f;通过使用 BGP丰富的路由属性&#xff0c;就可以得到解决。 属性分类…

GRU-PFG:利用图神经网络从股票因子中提取股票间相关性

“GRU-PFG: Extract Inter-Stock Correlation from Stock Factors with Graph Neural Network” 论文地址&#xff1a;https://arxiv.org/pdf/2411.18997 摘要 股票预测模型可以分为两个主要类别&#xff1a;第一类&#xff0c;例如GRU和ALSTM&#xff0c;这些模型仅基于股票…

Vue 按键生成多个表单

本文通过 vueele&#xff0c;通过循环ref的方法生成多个表单,代码如下&#xff1a; <template><div><el-button click"addText" style"margin: 15px 0; ">添加字段</el-button><div v-for"item, index in dataList"…

CSP初赛知识学习计划(第一天)

计算机知识全解析 一、计算机的发展历程 计算机的发展堪称一部波澜壮阔的科技史诗&#xff0c;其源头可追溯至古老的计算工具。早期&#xff0c;为了满足人类在天文、历法计算以及商业贸易中对数据处理的需求&#xff0c;算盘、算筹等手动计算器械应运而生&#xff0c;它们依…

滑动窗口。

1456 定长子串中元音的最大数目 采用滑动窗口。每次移动一个位置&#xff0c;判断当前窗口内的子串内目标元素的个数&#xff0c;若比之前更大就更新结果。 如何判断是否更新结果&#xff1f;也即&#xff0c;如何判断当前窗口内所含目标元素个数&#xff0c;是否为遍历到这个…

FastAPI 响应模型与自定义响应

FastAPI 响应模型与自定义响应 &#x1f4da; 目录 &#x1f3a8; 自定义响应类型概述&#x1f5c2;️ 文件上传与下载详解&#x1f36a; 自定义响应头与 Cookie 配置 &#x1f3a8; 1. 自定义响应类型概述 在 FastAPI 中&#xff0c;自定义响应类型是开发者实现更丰富输出…