如何使用AJAX结合Django实现数据库消息实时获取与渲染
Django + AJAX 定时轮询实现无刷新实时消息方案
一、后端增量消息接口开发
你已经完成了消息发送逻辑,现在只需要新增一个增量拉取新消息的接口即可,接口要同步返回关联的用户头像、全名、发送时间字段,只返回客户端上次拉取时间点之后的新消息,避免重复传输旧数据。
参考视图代码:
# views.py from django.http import JsonResponse from django.contrib.auth.decorators import login_required from .models import Message import json @login_required def get_new_messages(request): # 接收前端传递的本地最新消息ID,作为新消息的筛选起点 request_params = json.loads(request.body) last_msg_id = request_params.get('last_msg_id', 0) # 筛选ID大于last_msg_id的新消息,用select_related一次性联表查询用户、Profile数据,避免N+1查询 new_messages = Message.objects.filter( id__gt=last_msg_id # 多聊天室场景可追加房间ID过滤条件:room_id = request_params.get('room_id') ).select_related('sender', 'sender__profile').order_by('send_time') # 序列化消息数据 msg_list = [] for msg in new_messages: msg_list.append({ 'id': msg.id, 'content': msg.content, 'sender_fullname': msg.sender.profile.full_name, # 头像做兜底,未上传时返回默认头像 'sender_avatar': msg.sender.profile.avatar.url if msg.sender.profile.avatar else '/static/images/default-avatar.png', 'send_time': msg.send_time.strftime('%Y-%m-%d %H:%M'), 'is_self': msg.sender == request.user }) return JsonResponse({ 'code': 0, 'new_messages': msg_list, 'latest_msg_id': new_messages.last().id if new_messages.exists() else last_msg_id })
配置对应路由:
# urls.py from django.urls import path from . import views urlpatterns = [ # 原有发送消息等路由保留 path('api/messages/new/', views.get_new_messages, name='get_new_messages'), ]
二、前端轮询与消息渲染逻辑
前端用setInterval实现定时轮询即可,核心逻辑是维护一个本地最新消息ID,每次请求把这个ID传给后端,拿到新消息后循环追加到消息列表,不需要每次刷新整个列表。
首先参考单条消息的HTML结构,需要包含头像、昵称、发送时间、消息内容几个元素:
<!-- 消息容器,所有消息都追加到这个节点内 --> <div class="message-container" id="messageContainer"> <!-- 页面初始加载时渲染的历史消息,给最后一条消息加data-id属性存最新消息ID --> <!-- 单条消息结构示例 <div class="message-item" data-id="12"> <img src="/static/images/default-avatar.png" class="msg-avatar"> <div class="msg-body"> <div class="msg-meta"> <span class="sender-name">张三</span> <span class="send-time">2024-05-20 14:30</span> </div> <div class="msg-content">测试消息内容</div> </div> </div> --> </div>
JS逻辑实现:
// 初始化本地最新消息ID,取页面已渲染的最后一条消息的ID,无历史消息则为0 let currentMaxMsgId = $('#messageContainer .message-item').last().data('id') || 0; // 轮询间隔设置为2000毫秒(2秒),可根据业务场景调整 const POLL_GAP = 2000; // 拉取新消息方法 function loadNewMessages() { $.ajax({ url: '/api/messages/new/', type: 'POST', contentType: 'application/json', data: JSON.stringify({ last_msg_id: currentMaxMsgId // 多房间场景追加room_id: 当前房间ID }), // 携带Django CSRF令牌避免403 headers: {'X-CSRFToken': getCookie('csrftoken')}, success: function(res) { if (res.code !== 0 || res.new_messages.length === 0) return; // 更新本地最新消息ID currentMaxMsgId = res.latest_msg_id; // 循环遍历新消息,逐条追加到容器末尾 res.new_messages.forEach(msg => { const msgNode = ` <div class="message-item ${msg.is_self ? 'self' : 'other'}" data-id="${msg.id}"> <img src="${msg.sender_avatar}" class="msg-avatar" alt="${msg.sender_fullname}"> <div class="msg-body"> <div class="msg-meta"> <span class="sender-name">${msg.sender_fullname}</span> <span class="send-time">${msg.send_time}</span> </div> <div class="msg-content">${msg.content}</div> </div> </div> `; $('#messageContainer').append(msgNode); }); // 追加完成后自动滚动到底部 $('#messageContainer').scrollTop($('#messageContainer')[0].scrollHeight); } }) } // 页面加载完成后启动轮询 $(function() { // 进入页面先拉一次,避免等待一个轮询周期才看到新消息 loadNewMessages(); // 启动定时器 setInterval(loadNewMessages, POLL_GAP); }) // Django官方提供的获取CSRF Cookie工具方法 function getCookie(name) { let cookieValue = null; if (document.cookie && document.cookie !== '') { const cookies = document.cookie.split(';'); for (let i = 0; i < cookies.length; i++) { const cookie = cookies[i].trim(); if (cookie.substring(0, name.length + 1) === (name + '=')) { cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); break; } } } return cookieValue; }
三、注意事项
- 轮询间隔不要设置过短,1秒以下的间隔会大幅提升后端请求压力,普通场景2-3秒的延迟用户基本无感知
- 不要每次拉取全量消息,必须基于消息ID做增量拉取,否则消息量累计后接口响应速度会严重下降
- 自己发送消息的逻辑,发送成功后可以直接把消息插入本地DOM,不需要等轮询拉取,实现发送即时显示
- 可以通过
document.hidden属性监听页面可见性,页面切到后台时暂停轮询,切回后恢复,减少无效请求 - 消息样式可以通过给
.self和.other类加不同CSS实现自己发的消息靠右、别人发的靠左的效果
内容的提问来源于stack exchange,提问作者Destiny Franks
相关产品推荐
相关产品推荐

