Django模拟新浪微博的@功能 (转载)

  出处

在论坛加了个类似微博的@功能,在回复帖子的时候可以@系统中的用户,被@的用户可以收到自己被@的通知可以做出相应的处理。

 

关于model

 1 #-*- coding:utf-8 -*-
 2 from django.db import models
 3 from django.contrib.auth.models import User
 4 import datetime
 5 from geek.geekchallenge.models import *
 6 from django.db.models.signals import post_save
 7 from django.dispatch import receiver
 8 from django.contrib.contenttypes.models import ContentType
 9 from django.contrib.contenttypes import generic
10 import re
11 class Reply(models.Model):
12     thread = models.ForeignKey(Thread, verbose_name=u"所属帖子")
13     author = models.ForeignKey(Team, verbose_name=u"回复作者")
14     content = models.TextField(verbose_name=u"回复内容")
15     submit_time = models.DateTimeField(verbose_name=u"发表时间", auto_now_add=True)
16     update_time = models.DateTimeField(verbose_name=u"更新时间", blank=True, null=True, editable=False)
17     events = generic.GenericRelation('Event')
18     def __unicode__(self):
19         return self.submit_time.strftime("%Y-%m-%d %H:%m:%S")
20     class Meta:
21         verbose_name_plural = u"帖子回复"
22 @receiver(post_save, sender=Reply, dispatch_uid="ashin_unique_identifier")
23 def post_save_handler(sender, instance, **kwargs):
24     reply = instance
25     team_name_pattern = re.compile('(?<=@)(\w+)', re.UNICODE)
26     at_team_names = set(re.findall(team_name_pattern, reply.content))
27     if at_team_names:
28         for at_team_name in at_team_names:
29             if at_team_name != reply.author.user.username:
30                 try:
31                     at_team = User.objects.get(username=at_team_name)
32                     event = Event(author=reply.author.user, event=reply, at_team=at_team)
33                     event.save()
34                 except:
35                     pass
36     elif reply.author != reply.thread.author:
37         event = Event(author=reply.author.user, event=reply, at_team=reply.thread.author.user)
38         event.save()
39 class Event(models.Model):
40     content_type = models.ForeignKey(ContentType, verbose_name=u"被触发的模型")
41     object_id = models.PositiveIntegerField(verbose_name=u"被触发模型ID")
42     author = models.ForeignKey(User, verbose_name=u"事件发起者", related_name="author")
43     event = generic.GenericForeignKey('content_type', 'object_id')
44     at_team = models.ForeignKey(User, verbose_name=u"提到的人", related_name="at_team")
45     #两个外键都指向User必须使用related_name参数
46     submit_time = models.DateTimeField(verbose_name=u"@时间", auto_now_add=True)
47     is_readed = models.BooleanField(verbose_name=u"已读", default=False)
48     is_deleted = models.BooleanField(verbose_name=u"已被删除", default=False)
49     def __unicode__(self):
50         return u"%s在回复%s的帖子《%s》中提到了%s"%(self.author, self.event.thread.author, self.event.thread, self.at_team)
51     class Meta:
52         verbose_name_plural = u"回复新闻"
53         ordering = ["-submit_time"]

 

 

在用户按钮处显示有多少条未读@消息

 1 <script>
 2     function get_new_at_num(){
 3         $.get('/forum/new-at-num/',function(data){
 4             if (data != 0){
 5                 $('#at_tip').text('('+data+'条@我未读)');
 6                 $('#id_global_at').text(' ('+data+')');
 7             }
 8             else{
 9                 $('#at_tip').text('');
10                 $('#id_global_at').text('');
11             }
12             window.setTimeout(get_new_at_num, 5000)
13         });
14     };
15     $(function(){
16         get_new_at_num();
17     });
18 </script>
19 <div class="btn-group pull-right">
20     <a class="btn dropdown-toggle" data-toggle="dropdown" href="">
21         <i class="icon-user"></i>{% if request.user.is_authenticated %} {{request.user.username}} <span id="at_tip"></span>{% else %} 游客{% endif %}
22         <span class="caret"></span>
23     </a>
24     <ul class="dropdown-menu">
25         {% if request.user.is_authenticated %}
26         <li><a href="/team-info/"><i class="icon-cog"></i> 团队信息</a></li>
27         <li><a href="/forum/my-threads/"><i class="icon-th-list"></i> 我的帖子</a></li>
28         <li><a href="/forum/at-me/"><i class="icon-envelope"></i> @我的回复<font color="red" id="id_global_at"></font></a></li>
29         <li class="divider"></li>
30         <li><a href="/accounts/logout/"><i class="icon-off"></i> 退出</a></li>
31         {% else %}
32         <li><a href="/accounts/register/"><i class="icon-pencil"></i> 注册</a></li>
33         <li class="divider"></li>
34         <li><a href="/accounts/login/"><i class="icon-leaf"></i> 登录</a></li>
35         {% endif %}
36     </ul>
37 </div><!-- userbtn -->

 

 

ajax每隔5秒请求一次服务器返回未读消息的条数,有就显示在页面上

 

1 @login_required
2 def new_at_num(request):
3     unread_count = Event.objects.filter(at_team = request.user, is_deleted=False, is_readed = False).count()
4     return HttpResponse(unread_count)

@的消息页面

 

页面代码

 1 <div>
 2     <p>当前位置: <a href="/forum/">论坛首页</a> » @我的回复</p>
 3     <p>共 <b id="id_all_count">{{results|length}}</b> 条, 未读 <b id="id_unread_count">{{unread_count}}</b> 条</p>
 4 </div>
 5 <div class="span10">
 6             {% if paged_events.object_list %}
 7             <table class="table table-hover table-striped">
 8                 <tr id="id_news_table_head">
 9                     <th>点击查看@我的回复</th>
10                     <th>@我的时间</th>
11                     <th>阅读状态</th>
12                     <th>操作</th>
13                 </tr>
14                 {% for news in paged_events.object_list %}
15                 <tr id="id_news_{{news.id}}">
16                     <td><a href="/forum/{{news.event.thread.forum.id}}/{{news.event.thread.id}}/?reading={{news.id}}#reply-{{news.event.id}}">{{ news.author }}在回复{{ news.event.thread.author}}的帖子《{{news.event.thread.title}}》中提到了您</a></td>
17                     <td>{{ news.submit_time|date:"Y-m-d H:i:s"}}</td>
18                     <td>{% if news.is_readed %}已读{% else %}<font color="red" id="id_unread_{{news.id}}">未读</font>{% endif %}</td>
19                     <td><button id="id_readed_news_{{news.id}}" onclick="readed_news('{{news.id}}');" class="btn btn-mini btn-success" {% if news.is_readed %}disabled{% endif %}>设为已读</button> <button id="id_delete_news" onclick="delete_news('{{news.id}}');" class="btn btn-mini btn-warning">删除记录</button></td>
20                 </tr>
21                 {% endfor %}
22             </table>
23             {% endif %}
24 </div><!--span10-->
25 <script>
26     function readed_news(id){
27         var url = '/forum/readed-at-me/'+id+'/';
28         $.post(url, {'csrfmiddlewaretoken':'{{ csrf_token }}'}, function(data){
29             if (data=='success'){
30                 $('#id_unread_'+id).attr('color', '').text('已读');
31                 $('#id_readed_news_'+id).attr('disabled', 'true');
32                 $('#id_unread_count').text($('#id_unread_count').text()-1)
33             }else{
34                 alert('操作失败!');
35             }
36         });
37     }
38     function delete_news(id){
39         var url = '/forum/delete-at-me/'+id+'/';
40         $.post(url, {'csrfmiddlewaretoken':'{{ csrf_token }}'}, function(data){
41             if(data=='success'){
42                 if ($('#id_unread_'+id).text() == '未读'){
43                     $('#id_unread_count').text($('#id_unread_count').text()-1)
44                 }
45                 $('#id_news_'+id).remove();
46                 $('#id_all_count').text($('#id_all_count').text()-1)
47             }else{
48                 alert('操作失败!');
49             }
50         });
51     }
52 </script>

 

 

处理函数

 1 @login_required
 2 def at_me(request):
 3     #最新帖子
 4     latest_threads = Thread.objects.all()[:5]
 5     #回复事件
 6     results = Event.objects.filter(at_team = request.user, is_deleted=False)
 7     unread_count = Event.objects.filter(at_team = request.user, is_deleted=False, is_readed = False).count()
 8     #分页
 9     events_paginator = Paginator(results, 15)
10     try:
11         page = int(request.GET.get('page', 1))
12     except ValueError:
13         page = 1
14     try:
15         paged_events = events_paginator.page(page)
16     except (EmptyPage, InvalidPage):
17         paged_events = events_paginator.page(events_paginator.num_pages)
18     return render_to_response('at-me.html', {"results":results,'unread_count':unread_count, 'latest_threads':latest_threads, "paged_events":paged_events, "events_paginator":events_paginator}, context_instance=RequestContext(request))
19 @login_required
20 def readed_news(request, news_id):
21     news = get_object_or_404(Event, id=news_id, at_team=request.user)
22     if request.method == 'POST':
23         news.is_readed=True
24         news.save()
25         return HttpResponse("success")
26     return HttpResponse("error")
27 @login_required
28 def delete_news(request, news_id):
29     news = get_object_or_404(Event, id=news_id, at_team=request.user)
30     if request.method == 'POST':
31         news.is_deleted = True
32         news.save()
33         return HttpResponse("success")
34     return HttpResponse("error")

 

 

在回复中@用户

 

实现代码:

 1 <script src="/static/js/userAutoTips.js"></script>
 2 <style type="text/css" >
 3     ol, ul { list-style: none outside none; }
 4     .recipients-tips{ font-family:Tahoma, Arial;position:absolute; background:#282828; z-index:2147483647; padding:2px; border:2px solid #33b5e5; display:none;}
 5     .recipients-tips li a{display:block; padding:2px 5px; cursor:pointer;}
 6     .autoSelected{background:#131517;}
 7 </style>
 8 <script type="text/javascript">
 9     userAutoTips({id:'id_content'});
10 </script>

 

 

用了网上找的一个别人写好的只要检测到textarea中有@就会显示列表的代码,自己该了一些设置和获取最近活跃的10位用户

 1 @login_required
 2 def nearest_users(request):
 3     threads = Thread.objects.all()
 4     replies = Reply.objects.all()
 5     users = [thread.author.user.username for thread in threads]
 6     users.extend([reply.author.user.username for reply in replies])
 7     users = set(users)
 8     if request.user.username in users:
 9         users.remove(request.user.username)
10     user_name_list = []
11     for user in users:
12         user_name_list.append({}.fromkeys(('user', 'name'), user))
13     show_count = 15
14     if len(user_name_list) > show_count:
15         user_name_list = user_name_list[:show_count]
16     data = simplejson.dumps(user_name_list)
17     return HttpResponse(data, mimetype="application/json")

 

 

UserAutoTips.js放到微盘了:

http://vdisk.weibo.com/s/naFdb

 

posted @ 2013-10-13 16:02  枫桦宁  阅读(360)  评论(0编辑  收藏  举报