当前位置: 首页 > article >正文

每天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)

七、部署注意事项

  1. 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)],
        },
    },
}
  1. 静态文件处理
# 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.kler.cn/a/464726.html

相关文章:

  • 认识一下,轻量消息推送 Server-Sent Events
  • 你喜欢看哪类的网上视频教程?
  • 问题清除指南|关于num_classes与 BCELoss、BCEWithLogitsLoss 和 CrossEntropyLoss 的关系
  • FPGA基本语法与使用
  • 海南省大数据发展中心:数据资产场景化评估案例手册(第二期)
  • 【小程序开发】- 小程序版本迭代指南(版本发布教程)
  • 招银网路Java后端一面,难度有点大!
  • 多光谱图像的处理和分析方法有哪些?
  • 应用层协议(Https)(超详解)
  • 【HarmonyOS】解决自定义弹框和键盘之间安全距离的问题
  • react axios 优化示例
  • 【大模型系列】MultiUI(2024.11)
  • 七大排序算法:插入排序、希尔排序、选择排序、冒泡排序、堆排序、快速排序、归并排序
  • 集线器,交换机,路由器,mac地址和ip地址知识记录总结
  • HTML——47.元素类型
  • 【机器学习】机器学习的基本分类-自监督学习-生成式方法(Generative Methods)
  • 七款领先的网络准入控制解决方案分享:智能准入,安全无忧
  • (NDSS2024)论文阅读——仅低质量的训练数据?用于检测加密恶意网络流量的稳健框架
  • Apache Dubbo反序列化漏洞
  • JDK的运作原理
  • 做一套手机UI自动化测试的全套系统,支持对Android、ios进行UI自动化测试,使用什么样的后端、前端、UI自动化框架、持续集成和部署方案
  • vue.js 非父子通信-事件总线
  • 动态规划解决不同的二叉搜索树问题
  • 游戏社交趋势下,游戏语音再升级!
  • Springboot的自动配置原理?
  • LeetCode题解:2625. 扁平化嵌套数组,递归