在本篇博客中,我们将继续围绕着Redis做文章。由于Redis的内存存储特性,其适合用于存储那些变动频繁的数据,如博客点赞数、站内消息等。我们在这篇博客中将实现用户消息系统,即当有人给博客点赞或发表评论时,博客作者将收到站内消息,且在主页上会看到目前未读的消息数目。若有未读的消息,则消息数会加粗显示
用户消息系统分为两部分:消息发布和消息管理。由于当博客被评论或被点赞都会发布消息,因此发布消息的操作是一个高频操作,我们使用redis来发布消息。
首先来修改myblog/settings.py,向其中加入名为RedisKey的字典,用于保存redis中key的各种后缀:
# myblog/settings.py
# ...
RedisKey = {'TITLEKEY':'title',
'CONTENTKEY':'content',
'READCOUNTKEY':'readcount',
'COMMENTCOUNTKEY':'commentcount',
'UNREADMSGKEY':'unreadmsg',
'THUMBUPKEY':'thumbup',
'THUMBCOUNTKEY':'thumbupcount',
'READBLOGKEY':'readblog'
}
# ...
然后我们在myblog目录下建立myutil.py文件,这个文件用于存储一些通用函数,这里我们加入generateKey函数,用于封装生成rediskey的操作:
# myblog/myutil.py
# -*- coding=utf-8 -*-
def generateKey(pkey,keykind):
return str(pkey) + '_' + str(keykind)
由于每个用户可以同时收到多条消息,我们采用redis的list来存储消息。每当博客被评论或被点赞时,后台程序都会向这个用户所对应的消息列表中添加一条,并在之后的消息管理界面中显示出来。
我们修改blogs/views.py,向saveComment函数中添加以下代码:
# blogs/views.py
# ...
# Publish message to auther when new comment added(with redis)
messagekey = generateKey(blog.auther.username,RedisKey['UNREADMSGKEY'])
message_content = auther.username + u'评论了博客' + blog.title + u'于' + str(datetime.datetime.now())
redis.lpush(messagekey,message_content)
# ...
在这里,我们使用刚才的generateKey函数生成每个用户的messagekey,为用户名_unreadmsg。因为用户名一定是唯一的,所以整个key的唯一性可以保证。message_content为要发表的消息内容,这里的auther.username为评论的作者的用户名,而不是博客的作者。在拼接好消息内容后,我们就可以使用redis.lpush函数将消息放入博客作者的消息列表中。lpush的含义是,将value放入key指向的list的最左边,即最新的消息永远是list的第一个元素。
我们已经实现了消息的发布,下面来看看如何显示信息以及处理信息。信息的显示包括在两个页面中:主页和用户资料页。在主页中,如果某个用户有了新的消息,其消息个数将会显示在用户名的旁边,并以加粗字体突出,如图所示:
可以看到,该用户有6条消息。点击“消息(6)”,可以进入消息管理页面查看每条消息,并可将所有消息设为已读:
点击“全部设置为已读”后,消息消失,主页的消息也不再加粗显示:
为了实现以上的功能,我们需要在Users App添加消息箱页面,以及修改主页的前端部分代码。首先来看Users App的消息箱代码:
# users/views.py
# ...
# messagebox
def messagebox(request):
try:
currentUser = Users.objects.get(username=request.session['username'])
pool = ConnectionPool(host='localhost', port='6379', db=0)
redis = StrictRedis(connection_pool=pool)
messagekey = generateKey(currentUser.username,RedisKey['UNREADMSGKEY'])
if redis.exists(messagekey):
infos = redis.lrange(messagekey,0,redis.llen(messagekey)-1)
msginfos = []
for msg in infos:
msg = msg.decode()
msginfos.append(msg)
messages = {'infos': msginfos}
else:
messages = {}
except Exception as e:
messages = {}
return render(request,'users/messagebox.html',messages)
def setreaded(request):
try:
currentUser = Users.objects.get(username=request.session['username'])
messagekey = generateKey(currentUser.username, RedisKey['UNREADMSGKEY'])
pool = ConnectionPool(host='localhost', port='6379', db=0)
redis = StrictRedis(connection_pool=pool)
if redis.exists(messagekey):
redis.delete(messagekey)
except Exception as e:
pass
return HttpResponseRedirect(reverse('users:messagebox'))
# ...
messagebox为消息箱函数。该函数的作用就是从redis中拿到当前用户的消息列表,并将其渲染到前端;而对于没有消息的用户,则传入一个空字典作为渲染参数。我们使用redis.lrange(key,start,end)函数来拿到消息列表中的所有元素。该函数的作用为,从key所指向的list中拿取从start到end的元素,这里我们要拿取全部元素,因此start为0,end为llen(messagekey)-1,llen为消息列表的长度。
setreaded为全部设置为已读的函数,该函数的作用是清空当前用户的消息列表,直接使用redis.delete就好。
我们要在users/urls.py为这两个函数添加url:
# users/urls.py
# ...
urlpatterns = [
# ...
url(r'^messagebox/$',views.messagebox,name='messagebox'),
url(r'^setreaded/$',views.setreaded,name='setreaded')
]
然后我们要编写消息箱前端部分的代码。在Users App的页面文件夹中添加messagebox.html,输入以下代码:
<!--messagebox.html-->
{% extends "userTemplate.html" %}
{% block content %}
<div id="content" class="list">
<a href="{% url 'users:setreaded' %}" >全部设置为已读</a>
{% if infos %}
<ul>
{% for info in infos %}
<div class="articlelist">
{{ info }}
</div>
{% endfor %}
</ul>
{% else %}
<p>没有消息</p>
{% endif %}
</div>
{% endblock %}
同时修改userTemplate.html,为消息箱添加入口:
<!--userTemplate.html-->
<!--......-->
<span><a href="{% url 'users:messagebox' %}">消息箱</a></span>
<!--......-->
最后,修改myblog/index.html,在主页上显示消息个数并加粗:
<!--index.html-->
<!--......-->
{% block nav %}
<a href="{% url 'index' %}">首页</a>
{% if curruser.username == "anony" or curruser.username == "" %}
<a href="{% url 'users:userregister' %}">用户注册</a>
<a href="{% url 'users:userlogin' %}">用户登录</a>
{% else %}
用户:{{ curruser.username }}
<a href="{% url 'users:messagebox' %}">
{% if msgcount != 0 %}
<strong>消息({{ msgcount }})</strong>
{% else %}
消息({{ msgcount }})
</a>
{% endif %}
<a href="{% url 'users:logoff' %}">退出登录</a>
<a href="{% url 'blogs:addblog' %}">写博文</a>
{% endif %}
{% endblock %}
<!--......-->
这里为了能使大家看清新加代码的位置,我把nav块的代码一并放上来,其中加粗部分是我们要新加的代码。当然,我们还要修改myblog/views.py中的index函数,把msgcount渲染到前端:
# myblog/views.py/index
# ...
messagekey = generateKey(username,RedisKey['UNREADMSGKEY'])
if redis.exists(messagekey):
msgcount = redis.llen(messagekey)
else:
msgcount = 0
pool.disconnect()
content = { 'blog_list':blogList,
'curruser':user,
'msgcount':msgcount
}
# ...
这样,我们就实现了用户消息系统。此时,若你的博客被人评论,则在主页上会看到有未读消息的显示。
在这篇博文中,我们使用redis实现了一个简单的用户消息系统。在下篇博文中,我们将继续使用redis实现博客的点赞功能,并且使用cookie修复一个博客阅读数被重复统计的bug,希望大家继续关注~