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

Django中如何让信号接收器监听特定用户信号并推送审核通知

解决方案

你当前采用post_save信号实现的思路存在两个天然缺陷:

  • 信号属于模型层逻辑,和请求生命周期完全解耦,无法直接获取执行审核操作的管理员身份、也拿不到对应的request对象
  • 原生Django的messages框架绑定当前请求的用户,无法直接跨用户给博文作者发送消息,也无法实现无需刷新的跨标签页实时通知

以下是两种适配不同需求的实现方案:


方案一:无额外依赖的非实时实现(用户刷新页面即可看到通知)

不需要用到信号,把通知触发逻辑直接迁移到审核操作对应的视图层即可,如果你用Django自带的Admin做审核,直接重写BlogPost对应的ModelAdmin的save_model方法即可:

from django.contrib import admin
from django.contrib import messages
from django.contrib.sessions.models import Session
from django.utils import timezone
from .models import BlogPost

class BlogPostAdmin(admin.ModelAdmin):
    def save_model(self, request, obj, form, change):
        # 先获取修改前的审核状态
        original_approved = BlogPost.objects.get(pk=obj.pk).approved if change else False
        super().save_model(request, obj, form, change)
        # 仅当审核状态发生变化时触发通知
        if change and original_approved != obj.approved:
            author = obj.user
            # 遍历所有未过期的session,找到作者的所有登录会话
            valid_sessions = Session.objects.filter(expire_date__gte=timezone.now())
            for session in valid_sessions:
                session_data = session.get_decoded()
                if session_data.get('_auth_user_id') == str(author.id):
                    # 按照messages框架的存储格式写入作者的session
                    if '_messages' not in session_data:
                        session_data['_messages'] = []
                    if obj.approved:
                        msg_item = (messages.SUCCESS, '你的博文已审核通过', '')
                    else:
                        msg_item = (messages.ERROR, '你的博文未通过审核', '')
                    session_data['_messages'].append(msg_item)
                    # 保存修改后的session
                    session.session_data = Session.objects.encode(session_data)
                    session.save()

admin.site.register(BlogPost, BlogPostAdmin)

这种方案下,作者无论在哪个标签页,只要刷新页面就会自动加载session中存储的消息并显示,符合Django原生messages框架的使用逻辑,不需要额外安装依赖。


方案二:支持实时跨标签页推送的实现

如果需要做到审核通过后,作者已经打开的所有页面不需要刷新就能立刻弹出通知,需要使用WebSocket协议实现主动推送,Django生态中常用Channels库实现WebSocket功能,实现步骤如下:

  1. 安装配置Django Channels,配置Redis作为频道层存储(生产环境)
  2. 编写WebSocket消费者,每个登录用户连接后自动加入专属的通知频道组
  3. 审核状态变化时,向作者的专属频道组推送通知消息
  4. 所有页面引入前端JS代码,监听WebSocket消息并弹出提示

核心代码示例

消费者代码(consumers.py)

import json
from channels.generic.websocket import AsyncWebsocketConsumer

class NotificationConsumer(AsyncWebsocketConsumer):
    async def connect(self):
        self.user = self.scope["user"]
        if not self.user.is_authenticated:
            await self.close()
            return
        # 每个用户对应一个专属通知组
        self.group_name = f"user_{self.user.id}_notify"
        await self.channel_layer.group_add(self.group_name, self.channel_name)
        await self.accept()

    async def disconnect(self, close_code):
        await self.channel_layer.group_discard(self.group_name, self.channel_name)

    # 接收组推送的消息并发送给前端
    async def send_notify(self, event):
        await self.send(text_data=json.dumps({
            "level": event["level"],
            "content": event["content"]
        }))

审核视图中推送消息的逻辑

from channels.layers import get_channel_layer
from asgiref.sync import async_to_sync

# 在审核状态变化的逻辑中加入以下代码
channel_layer = get_channel_layer()
author = obj.user
if obj.approved:
    level, content = "success", "你的博文已审核通过"
else:
    level, content = "error", "你的博文未通过审核"
# 向作者的专属组推送消息
async_to_sync(channel_layer.group_send)(
    f"user_{author.id}_notify",
    {"type": "send_notify", "level": level, "content": content}
)

前端公共JS代码(所有页面引入)

const wsProtocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'
const notifySocket = new WebSocket(`${wsProtocol}//${window.location.host}/ws/notify/`)
notifySocket.onmessage = (e) => {
    const data = JSON.parse(e.data)
    // 可替换为项目中使用的提示组件逻辑,比如Toast、Alert等
    if (data.level === 'success') {
        alert(`成功:${data.content}`)
    } else {
        alert(`错误:${data.content}`)
    }
}

内容的提问来源于stack exchange,提问作者akimalamute

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.06 20:30:05