You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 06:06:55