关于本站
1、基于Django+Bootstrap开发
2、主要发表本人的技术原创博客
3、本站于 2015-12-01 开始建站
这几天琢磨怎么实现智能推荐文章。查阅了一些资料,发现不太适合使用协同算法实现智能推荐功能。
最后准备使用博文的相关关键字进行计算,猜测访客的阅读偏好,进而推荐相关的博文。
那么,需要给博文添加关键字。另外关键字有利于SEO优化。
打开Django中blog应用的models.py文件,添加Keyword模型和Blog添加Keyword多对多外键字段。
class Keyword(models.Model): """Keyword关键字模型""" keyword = models.CharField(max_length=64) create_time = models.DateTimeField(auto_now_add=True) def __unicode__(self): return u'%s' % (self.keyword) class Blog(models.Model): """Blog模型""" #其他字段省略不显示 keywords = models.ManyToManyField(Keyword, blank=True)
修改模型操作需要更新数据库:
>>> python manage.py makegrateions >>> python manage.py migrate
可以修改admin.py,显示Keyword并新增一些关键字。
#coding:utf-8 from django.contrib import admin from blog.models import Keyword @admin.register(Keyword) class KeywordAdmin(admin.ModelAdmin): """keywords admin""" list_display=('keyword', 'create_time')
这样我们就可以在后台管理界面给对应的博文新增关键字。关键字如何写这个大家可以网上搜索。
(当然,我是不会在后台管理界面修改。后面会结合我前面写的富文本框新增修改博文的功能处理。)
打开每篇博文时,需要把关键字输出到模版。修改view.py文件对应的方法:
#找到对应的响应打开博文页面的方法 def blog_show(request, id): #其他代码不显示 data = {} data["keywords"] = ','.join(map(lambda x:x.keyword, blog.keywords.all())) #其他代码不显示
该句代码把该篇博文的关键字用逗号隔开,写到keywords变量中。
再修改对应的模版页面,在head标签部分添加如下代码:
{%if keywords%}<meta name="keywords" content="{{keywords}}">{%endif%}
若没有keywords,就不写该句meta标签。
基础功能到这里为止,接下来结合标签输入插件看看高级炫酷的功能。
------- 手动分割线 -------
前面写了一个页面,用UEditor新增博文。
我需要完善该功能,在后面加上关键字编辑。最终效果如下:
底下部分是一个标签输入插件:bootstrap-tagsinput。该插件是基于bootstrap和jquery。
相关链接:http://bootstrap-tagsinput.github.io/bootstrap-tagsinput/examples/
该链接是一个示例,大家可以下载修改试试。我自己修改整理了一个示例:http://pan.baidu.com/s/1c1RoXiC。效果如下图:
除了bootstrap-tagsinput自身的功能(只有第一行的关键字),我还加一个推荐关键字的功能。这些推荐的关键字都来自Keyword表中的数据,方便我们输入。
修改该模版页面先添加相关css和js引用。
<link rel="stylesheet" href="/static/css/bootstrap/bootstrap.min.css"> <link rel="stylesheet" type="text/css" href="/static/css/bootstrap/bootstrap-tagsinput.css"> <script src="/static/js/jquery-1.11.3.min.js"></script> <script src="/static/js/bootstrap.min.js"></script> <script type="text/javascript" src="/static/js/bootstrap-tagsinput.min.js"></script>
在页面合适的位置,添加关键字部分的html代码:
<div class="keywords_content"> <div> <label for="keywords">关键字:</label> <input id="keywords" name="keywords" type="text" value="" data-role="tagsinput" placeholder="添加关键字 "/> </div> <div> <label for="recommend_keywords" style="margin-top:0.5em">推荐词:</label> <input type="hidden" id="keywords_index" value="0" tag="关键字页数标记"/> <div id="recommend_keywords"></div> <a href="javascript:get_keywords();" class="btn btn-default btn-xs">换一批</a> <a href="javascript:$('#keywords').tagsinput('removeAll');" class="btn btn-default btn-xs">全部清除</a> </div> </div>
当然,该标签在我一个form表单中。其中“换一批”按钮的代码那个函数下面再加上。
我需要添加一些推荐的关键字到recommend_keywords中。这里为了避免刷新页面,需要用ajax获取和加载数据。
在script部分,再加入如下设置:
$("#keywords").tagsinput({ maxTags: 10, //最多有10个标签 maxChars: 24, //每个标签最大长度 trimValue: true //标签名前后剔除空格 });
接着再写后端代码,获取推荐的关键字。因为关键字随着发表的博文会越来越多。需要分批获取,每次获取不同的关键字。该操作相当于分页操作。打开views.py文件,代码如下:
#coding:utf-8 from django.db.models import Count #个数统计 from django.core.paginator import Paginator #分页器 from django.views.decorators.csrf import csrf_exempt #排除csrf验证 from blog.models import Blog, Keyword #相关的模型 from django.http import Http404, HttpResponse import json #处理推荐关键字 @csrf_exempt def ajax_recommend_keywords(request): data = {} try: if request.method != 'POST': raise Exception('method error') #获取POST的json req_json = json.loads(request.body) keywords_index = int(req_json.get('keywords_index', '0')) + 1 #得到页数索引 keywords_each_num = int(req_json.get('keywords_each_num', '20')) #每页显示多少页 #个数修正 if keywords_each_num<5: keywords_each_num = 20 if keywords_each_num>50: keywords_each_num = 50 #关键字按使用次数排序 qs = Keyword.objects.annotate(blog_count=Count('blog')).order_by('-blog_count') paginator = Paginator(qs, keywords_each_num) page_all = paginator.num_pages #总页数 if keywords_index>page_all: keywords_index = 1 objectlist = paginator.page(keywords_index) #获取当前页面的对象集 data["code"] = 0 data["message"] = "OK" data["keywords_index"] = keywords_index data["keywords_list"] = ','.join(map(lambda x:x.keyword, objectlist)) except Exception as e: data["code"] = 1 data["message"] = e.message return HttpResponse(json.dumps(data), content_type = 'application/json')
用ajax访问该方法,其中需要当前页码keywords_index和每页个数keyword_each_num。
其中,关键字需要安装使用频率倒序排序。使用比较多的关键字优先显示。
qs = Keyword.objects.annotate(blog_count=Count('blog')).order_by('-blog_count')
这句代码效果相当与如下的SQL查询:
select blog_keyword.* from blog_keyword left join blog_blog_keywords on blog_keyword.id = blog_blog_keywords.keyword_id group by blog_keyword.id order by count(blog_blog_keywords.keyword_id) des
获取之后,在把数据用json返回给前端。格式如下:
{ "code":0, //状态码:0表示成功,非0表示出错 "message":"OK", //消息:主要用于出错显示返回的消息 "keywords_index":2, //关键字页码 "keywords_list":"python,linux,excel" //推荐的关键字,用逗号隔开 }
再添加url路由设置,就可以访问该方法。打开urls.py,加入如下代码:
url(r'^get_keywords$', blog_views.ajax_recommend_keywords, name='get_keywords'),
前端页面的get_keywords()方法代码如下:
function get_keywords(){ var json = {}; $('.tip-text').text(''); //每批关键字的个数 json['keywords_each_num'] = 10; //获取当前关键字页码 json['keywords_index'] = $('#keywords_index').val(); //ajax提交json $.ajax({ type: "POST", data: JSON.stringify(json), url: "{% url 'get_keywords' %}", cache: false, dataType: "json", success: function(json, textStatus) { if(json["code"]!=0){ $('.tip-text').text(json['message']); }else{ //更新列表 update_list(json['keywords_list'].split(',')); //更新页码标记 $('#keywords_index').val(json['keywords_index']); } }, error: function (XMLHttpRequest, textStatus, errorThrown) { $('.tip-text').text('获取推荐词列表出错,请稍后重试'); } }); } //更新列表 function update_list(arr){ $("#recommend_keywords").children().remove(); //移除所有子节点 //遍历添加子节点 for (var i = 0; i < arr.length; i++) { var str = '<a class="recommend_sub btn btn-info btn-xs">'+arr[i]+'</a>'; $("#recommend_keywords").append(str); }; //绑定事件 $("#recommend_keywords a").each(function(){ $(this).click(function(){$("#keywords").tagsinput('add', $(this).text())}); }); };
并在页面加载的时候,执行一次该方法。这样可以实现分批次从数据库获取关键字。
到这里,基本逻辑已经完成了。
先获取第1页的关键字。获取之后,页码加1记录在一个hidden标签中。下一次可以获取下一页的关键字。到了最后一页,再重新获取第1页的关键字。
我们可以直接点击推荐关键字列表,添加关键字。
最后,在保存博文的时候,获取这些关键字,并保存添加即可。
提交之前,还需要验证是否有填写关键字。这个简单,前端验证代码如下:
//关键字 if($('#keywords').val().length<=0){ $('.tip-text').text('尚未添加关键字'); return false; }
后端验证代码如下:
#检查和获取博客新增修改POST提交的数据 def _ajax_post_data(request): if request.method != 'POST': raise Exception('method error') #获取数据 #省略了其他代码,可参考我前面的博文 blog_keywords = request.POST.get('keywords', '').split(',') #省略了其他代码,可参考我前面的博文 #验证数据 #省略了其他代码,可参考我前面的博文 if len(blog_keywords)==0: raise Exception(u'尚未添加关键字') #返回数据 post_data = {} #省略了其他代码,可参考我前面的博文 post_data['blog_keywords'] = blog_keywords return post_data
省略的代码可以看我前面的博文:用UEditor新增博文。
接着再保存关键字。这里需要判断关键字是否存在,不存在就新增即可。
@check_admin def add_ajax(request): data = {} try: #检查和获取博客新增POST提交的数据 post_data = _ajax_post_data(request) #新增博文 blog = Blog() blog.caption = post_data['blog_title'] blog.author = request.user blog.content = post_data['blog_content'] blog.recommend = post_data['blog_recommend'] blog.save() #处理多对多,需要博文保存后才能处理,而新增后无需保存 #blog.tags.clear() for tag in Tag.objects.filter(id__in = post_data['tag_ids']): blog.tags.add(tag) #插入关键字 for keyword_text in post_data['blog_keywords']: try: keyword = Keyword.objects.get(keyword=keyword_text) except: keyword = Keyword(keyword=keyword_text) keyword.save() blog.keywords.add(keyword) #返回结果 data['success'] = True data['message'] = reverse('detailblog', args = [blog.id,]) except Exception as e: data['success'] = False data['message'] = e.message return HttpResponse(json.dumps(data), content_type = 'application/json')
哈哈,到这里不知道晕了没有。这些需要大家实践。只是看代码很容易晕。
新增关键字完整的代码和流程已经完成了。
等等,还没结束。
有新增博客,就有修改博客。同样,参考我前面的博文:我的网站搭建(第38天) 用UEditor编辑博文。
这里大部分代码和新增博客一样。只需要修改两个地方即可。
1)打开修改页面时,需要写入原有关键字的内容。
这个和给每篇博文写关键字meta标签一样。用逗号隔开,把关键字输出input标签的valu即可。
#后端代码 data["keywords"] = ','.join(map(lambda x:x.keyword, blog.keywords.all())) #前端代码 <input id="keywords" name="keywords" type="text" value="{{keywords}}" data-role="tagsinput" placeholder="添加关键字 "/>
2)后端保存修改博客的时候,因为是多对多处理,插入关键字之前需要删除原有的关键字。
在插入关键字之前,加入如下代码即可:
blog.keywords.clear()
终于把全部内容说完了,希望我有讲清楚。哈哈~