关于本站
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()
终于把全部内容说完了,希望我有讲清楚。哈哈~