1、安装Notifications

站内通知使用django-notifications-hq第三方库。执行如下命令安装django-notifications-hq:

 pip install django-notifications-hq

执行命令后,安装3个库。django-model-utils、django-notifications-hq、jsonfield

可以在Python安装目录Lib/site-packages找到notifications。以下开发基本都是查看notifications源码和其Github的帮助。

接着,打开Django项目的settings.py文件,在INSTALLED_APPS加入该应用:

INSTALLED_APPS = [# ... 其他省略不写'notifications',
]

再更新数据库,由于notifications已经makemigrations了,直接migrate更新同步数据库:

  1. python manage.py migrate notifications

再打开urls.py总路由设置,添加notifications的urls(貌似不加也行,我没有使用到)

  1. url(r'^notifications/', include('notifications.urls')),

2、评论或回复时发送消息通知

当然,不止在评论或回复时才发送消息通知。可以在任何地方发送消息通知,例如用户注册成功、用户第一次登录等等。主要看你的需求,基本原理都一样,我以django-comments库评论或回复作为例子。相关的django-comments开发可参考Django评论库开发专题。

此处不建议直接修改评论库提交评论的代码,可使用signals机制处理消息通知。

signals是Django一套信号机制,模型对象操作会产生一系列的信号。例如保存前、保存后。Django自动监控到这些信号会执行对应的代码。故,打开django-comments库的signals.py文件,在其中添加评论提交之后的处理代码。

django-comments库的路径同样在Python安装目录的Lib/site-packages中。由于我对该库修改比较多,已经复制全部代码到我的Django项目中。打开signals.py文件,可发现已经定义好了3个signals信号器。

#coding:utf-8
from django.dispatch import Signalcomment_will_be_posted = Signal(providing_args=["comment", "request"])
comment_was_posted = Signal(providing_args=["comment", "request"])
comment_was_flagged = Signal(providing_args=["comment", "flag", "created", "request"])

其中,comment_was_posted是评论保存之后监控的信号。我们将使用该信号,在该文件添加如下代码:

  1. #coding:utf-8
  2. from django.dispatch import receiver
  3. from django.shortcuts import get_object_or_404
  4. from notifications.signals import notify
  5. try:
  6. from django.apps import apps
  7. except ImportError:
  8. from django.db import models as apps
  9. from .models import Comment
  10. from . import get_model
  11. @receiver(comment_was_posted, sender=Comment)
  12. def send_message(sender, **kwargs):
  13. # 获取相关数据
  14. #print(kwargs)
  15. comment = kwargs['comment']
  16. request = kwargs['request']
  17. user = comment.user
  18. username = user.first_name or user.username
  19. # 获取评论的对象
  20. data = request.POST.copy()
  21. ctype = data.get("content_type")
  22. object_pk = data.get("object_pk")
  23. model = apps.get_model(*ctype.split(".", 1))
  24. target = model._default_manager.using(None).get(pk=object_pk)
  25. # 判断是评论还是回复,设置消息标题
  26. if int(comment.root_id) == 0:
  27. # 评论对象(博客,专题)
  28. content_object = comment.content_type.get_object_for_this_type(id=object_pk)
  29. recipient = content_object.author  # 被评论时,通知文章作者
  30. verb = u'[%s] 评论你了' % username
  31. else:
  32. # 被回复
  33. reply_to = get_object_or_404(get_model(), id=comment.reply_to)
  34. recipient = reply_to.user  # 被回复时,通知评论者
  35. verb = u'[%s] 回复你了' % username
  36. # 发送消息(level: 'success', 'info', 'warning', 'error')
  37. message = {}
  38. message['recipient'] = recipient            # 消息接收人
  39. message['verb'] = verb                      # 消息标题
  40. message['description'] = comment.comment    # 评论详细内容
  41. message['target'] = target                  # 目标对象
  42. message['action_object'] = comment          # 评论记录
  43. notify.send(user, **message)

这部分的代码是整个站内消息通知的核心。一部分一部分拆分讲解。

首先,signals的结构。receiver是绑定处理信号的方法,sender是该信号的发送者。基本结构如下:

  1. @receiver(comment_was_posted, sender=Comment)
  2. def send_message(sender, **kwargs):
  3. print(kwargs)  # 打印参看有哪些参数

可以打印kwargs查看有哪些参数。或者你可以查看该库的views/comments.py文件中的post_comment方法。在该方法的末尾可看到发送信号的代码:

从上图可看到评论保存前后各发送(send)两个信号。保存之后发送的signal参数有sender、comment、request。我们可以根据comment和request得到我们所需的数据。

在signals中获取被评论的对象就是通过comment获取,当然该代码不是我写的,参考comments.py的post_comments方法。

至于判断评论还是回复这部分代码可以忽略,这个是我修改django-comments库加入回复功能。

最后部分的代码,notify.send同样使用了signals。使用notifications的signals,可打开notifications源码查看。而前面的message中的数据都是notify所需的数据。这些参数不是都必须的,可根据自己项目的实际需求使用。记录target是为了知道评论哪篇博客;记录action_object是为了将评论和消息一一对应,才可根据评论对象找到对应的消息对象。

3、获取消息

上面的参数recipient是希望谁接到通知。notifications是和Django的用户系统绑定。若settings设置了AUTH_USER_MODEL,也自动使用自定义的用户系统。可通过User获取该用户相关的消息,例如:

  1. user = request.user
  2. user.notifications.all()  # 全部消息
  3. user.notifications.unread()  #未读消息
  4. user.notifications.read()  #已读消息

还可在模版中使用模版标签获得未读消息数:

  1. {% load notifications_tags %}
  2. {% notifications_unread as unread_count %}
  3. <span>你有{{unread_count}}条未读消息</span>

现需要将未读消息显示在导航栏的用户名旁边,如下所示:

问题我网站判断用户的登录状态是通过ajax加载页面之后判断的,非直接在底层模版中用模版标签判断。若同样在页面加载之后再通过ajax异步获取消息会很麻烦,代码耦合性较高。而模版页面用使用request.user,需要用render或render_to_reponse + RequestContext。例如:

  1. from django.shortcuts import render_to_response
  2. from django.template import RequestContext
  3. return render_to_response('index.html', data, context_instance=RequestContext(request))

以上等同于:

  1. from django.shortcuts import render
  2. return render(request, 'index.html', data)

当然选择使用render,render相当于render_to_response的简写。若你代码也需要在模版页面使用request.user,最好也改成render方式。然后再模版页面判断获取未读消息数,例如:

  1. {#判断是否有登录用户#}
  2. {% if request.user.is_authenticated %}
  3. {% notifications_unread as unread_count %}
  4. <span>
  5. 您好, {{request.user.username}}
  6. {#判断是否有未读消息#}
  7. {% ifnotequal unread_count 0 %}
  8. <span style="background-color:#d9534f">
  9. {{unread_count}}
  10. </span>
  11. {% endifnotequal %}
  12. </span>
  13. <ul class="dropdown-menu">
  14. {#如果是管理员#}
  15. {% if request.user.is_superuser %}
  16. <li><a href="{%url 'admin:index'%}">后台管理</a></li>
  17. {% endif %}
  18. <li>
  19. <a href="{%url 'user_info'%}">
  20. 用户中心
  21. {% ifnotequal unread_count 0 %}
  22. <span style="background-color:#d9534f">
  23. {{unread_count}}
  24. </span>
  25. {% endifnotequal %}
  26. </a>
  27. </li>
  28. <li><a href="{%url 'user_logout'%}">退出</a></li>
  29. </ul>
  30. {% else %}
  31. <a href="/user/login_page">登录/注册</a>
  32. {% endif %}

上面的{%url 'user_info'%}是进入我网站的用户中心页面。可在其中显示未读消息和已读消息,这里简单实现,先显示最多30条未读消息。

首先需要修改或者新增user_info对应的响应方法返回未读消息。核心代码如下:

  1. user = request.user
  2. unread = user.notifications.unread()[:30]
  3. data={}
  4. data['unread_list'] = unread  # 返回未读消息

对应的模版页面再处理unread_list,列举未读消息。

  1. <div class="unread_head">
  2. <span>您共有{{unread_list.count}}条未读消息</span>
  3. <a class="btn btn-info unread_btn"
  4. href="{%url 'user_mark_all_read'%}">
  5. 全部标记为已读
  6. </a>
  7. </div>
  8. <ul class="unread_list">
  9. {%for unread_item in unread_list%}
  10. <li id="unread_{{unread_item.id}}">
  11. <span>{{unread_item.timesince}}前 &gt; </span>
  12. <a href="{{unread_item.target.get_url}}?notification={{unread_item.id}}#F{{unread_item.action_object.id}}">
  13. {{unread_item.verb}}
  14. </a>
  15. <p class="unread_descript">{{unread_item.description}}</p>
  16. </li>
  17. {%endfor%}
  18. </ul>

这个模版页面也是我反复测试调整的结果,里面有些参数需要一一讲解。效果如下:

先看for循环部分。timesince属性是获取该消息是多久之前的消息;verb和description分别是消息的简要标题和内容;target是前面创建消息绑定的对象(博客或专题)。为了方便获取具体链接,在博客和专题的model类中分别加入获取具体对象的链接方法:

  1. from django.core.urlresolvers import reverse  # url逆向解析
  2. class Blog(models.Model):
  3. # 其余代码省略
  4. pass
  5. # 获取博客明细链接(根据具体情况写链接解析即可)
  6. def get_url(self):
  7. return reverse('detailblog', kwargs={'id':self.id})

大家可否发现,这个有两个链接user_mark_all_read和for循环中复杂的链接。如下讲解。

4、修改消息状态为已读

先看看上面for循环中构造的链接。该链接是消息具体指向位置。

由于我这里是评论或回复的通知消息,所以消息最终要指向评论或回复的具体位置。原本评论在邮件通知的链接如下:

  1. /subject/3#F168

#号前半部分是具体页面;F168是执行评论的锚点位置,在打开页面中得到该值并定位到评论位置。

当你打开该页面,需要修改本条未读消息为已读消息状态。

而在后台我接受不到#号后面的内容。于是在链接加入GET请求的参数notification,通过该参数获取具体的消息并修改消息状态。

那什么地方处理修改消息状态呢?当然是打开具体的博客或专题的处理方法中修改。为了不重复写冗余代码,我将修改消息状态的代码写成装饰器:

  1. #coding:utf-8
  2. from notifications.models import Notification
  3. # 修改未读消息为已读装饰器
  4. def notifications_read(func):
  5. def wrapper(request, *args, **kwargs):
  6. print(request.get_full_path())
  7. notify_key = 'notification'
  8. if request.GET.has_key(notify_key):
  9. try:
  10. # 获取消息
  11. notify_id = int(request.GET[notify_key])
  12. notify = Notification.objects.get(id=notify_id)
  13. # 标记为已读
  14. notify.unread = False
  15. notify.save()
  16. except ValueError:
  17. # int转换错误,不处理
  18. pass
  19. except Notification.DoesNotExist:
  20. # 消息不存在,不处理
  21. pass
  22. return func(request, *args, **kwargs)
  23. return wrapper

再对应的处理方法上加该装饰器,例如博客的具体页面处理方法:

  1. @notifications_read
  2. def blog_detail(request, id):
  3. # 博客响应方法的代码非主要,省略
  4. pass

还有上面有个user_mark_all_read链接,该链接是将所有未读消息修改为已读消息。对应响应方法如下:

#coding:utf-8
from django.http import HttpResponseRedirect
from django.core.urlresolvers import reverse # url逆向解析def user_mark_all_read(request):user = request.usernotifies = user.notifications.all()notifies.mark_all_as_read()  # 标记所有未读为已读return HttpResponseRedirect(reverse('user_info'))  # 重定向回用户中心

此处偷了一下懒,直接重定向回用户中心页面。请根据具体项目细节写代码。

5、收尾

还有个问题,之前通过邮件发送评论通知。其中的链接也需要加入notification参数,让用户打开具体页面时修改消息状态。

这时候需要用到前面创建消息使用的action_object了。前面将评论和消息通过该对象一一对应关联,所以在发送邮件通知的时候,通过评论id获取对应的消息通知id。若你也和我使用同样的逻辑机制,可参考如下代码:

from notifications.models import Notification
from django.contrib.contenttypes.models import ContentType# 此处已经有comment对象和具体页面的链接src_url可使用#判断评论是否有对应的消息通知(一条评论对应一条消息)
comment_content_type_id = ContentType.objects.get_for_model(comment).id
notifies = Notification.objects.filter(\action_object_content_type_id=comment_content_type_id, \action_object_object_id=comment.id)# 构造链接
if notifies.count() > 0:comment_url = u'%s?notification=%s#F%s' % (src_url, notifies[0].id, comment.id)
else:

Django站内消息通知相关推荐

  1. 以 B 站为例,聊聊站内消息系统的设计

    作者:guang19 使用过简书,知乎或 b 站的小伙伴应该都有这样的使用体验:当有其他用户关注我们或者私信我们的行为时,我们会收到相关的消息. 虽然这些功能看上去简单,但其背后的设计是非常复杂的,几 ...

  2. [UWP]实现一个轻量级的应用内消息通知控件

    [UWP]实现一个轻量级的应用内消息通知控件 原文:[UWP]实现一个轻量级的应用内消息通知控件 在UWP应用开发中,我们常常有向用户发送一些提示性消息的需求.这种时候我们一般会选择MessageDi ...

  3. 站内信息 php,站内消息_php教程

    php代码class MessageModel extends Model { public $_fields = array( //字段 'id' => 'Id', 'title' => ...

  4. [转贴]现在在做一个WEB的站内消息系统,从工具栏位置弹出一徐徐上升的窗口...

    现在在做一个WEB的站内消息系统, 想在用户登陆时, 如果有未读短消息 则从工具栏位置弹出一徐徐上升的窗口 显示提醒信息! <script language="JavaScript&q ...

  5. html站内消息列表,WebSocket实现站内消息实时推送

    关于WebSocket WebSocket是HTML5 开始提供的一种在单个TCP连接上进行全双工通讯的协议.什么是全双工?就是在同一时间可以发送和接收消息,实现双向通信,比如打电话.WebSocke ...

  6. Dynamics 365Online 应用内消息通知(In-app notifications)

    应用内通知其实一直是个刚需,但D365一直缺乏这个功能,如果客户有需求,我们只有通过自定义的方式实现,好在V9以后顶部栏可以自定义了,可以自己加Icon,实现通知的样式. 好在2022WAV1后,D3 ...

  7. async-excel整合站内信通知用户体验感满满

    前面的文章我们讲过消息中心站内信的实现 [消息中心] 那么本章我们来说说异步导入导出完成后,如何使用消息中心站内信的功能进行通知用户业务处理完成了 在async-excel中异步逻辑处理完成后会调用一 ...

  8. php mysql 站内消息_php如何开发网易云信消息抄送功能之聊天室消息保存到本地数据库...

    在开发APP时,需要使用到即时通讯功能.这里选择了网易云信.我们要将开发消息抄送功能,并且将聊天室的消息保存到本地的数据库中.其他的比如P2P聊天消息,群组聊天消息,群组操作,好友操作等消息,我这里就 ...

  9. 微服务架构集成RabbitMQ给用户推送消息(发送短信,发送邮件,发送站内信息)

    因为是分布式微服务项目,所以发送方在一个微服务,接收方在另外的一个微服务,在发送方,导入RabbitMQ依赖包 <!--RabbitMQ依赖--><dependency>< ...

最新文章

  1. 【C++】【五】循环链表
  2. 真执着 卡巴斯基和Palo Alto找到了BlackEnergy和ExPetr的相似代码
  3. boost::hana::extend用法的测试程序
  4. 编译php7.0.17报错提示 undefined reference to `libiconv_open'
  5. NXP(I.MX6uLL)DDR3实验——DDR3重要时间参数、时钟配置与原理图简析
  6. 各种浏览器css不兼容的写法
  7. LeetCode 58.最后一个单词的长度(python、c++)
  8. 双十一虽过,李宁老师视频课程优惠仍将继续
  9. kafka从头消费信息
  10. 灰度变换——反转,对数变换,伽马变换,灰度拉伸,灰度切割,位图切割
  11. android 动态壁纸 例子,android 动态壁纸实例(1)【转】
  12. 《如何让你爱的人爱上你》第一部分:第一印象
  13. windowsXP操作系统的基本功能
  14. mysql每五分钟取一次数据_mysql – 给定时间内每5分钟的平均数据
  15. 【Keil】十字路口车辆行人红绿灯实现 _单片机Keil开发
  16. Spring项目使用H2内存数据库做单元测试
  17. Java设计模式之外观模式(门面模式,迪米特法则的具体实现,抽象外观类改进)
  18. 国内各大短视频平台去水印下载内容,新年限时免费使用。
  19. 1022: [SHOI2008]小约翰的游戏John
  20. 桌面计算机和笔记本被称为,网络上常见的PC是什么意思?深度解读计算机发展史...

热门文章

  1. c语言运算符优先级结合,C语言运算符优先级及结合性
  2. (实战)Node.js 实现抢票小工具短信通知提醒
  3. 我秃了!唯一索引、普通索引我该选谁?
  4. 高通量测序与杂交优势
  5. adg的archive出现gap,使用增量恢复的方式进行恢复adg,RMAN-06094: datafile 1 must be restored
  6. 周金涛:人生就是一次康波
  7. springMVC+阿里云API = 实现发送手机验证码短信
  8. python 斗地主发牌_tkinter模拟斗地主发牌
  9. win11添加右键在此处打开命令窗口
  10. SketchUp: Modeling Exteriors from Photos SketchUp:从照片建模外部 Lynda课程中文字幕