关于本站
1、基于Django+Bootstrap开发
2、主要发表本人的技术原创博客
3、本站于 2015-12-01 开始建站
前面开发了用UEditor新增博文。在准备开发用UEditor修改博文时,发现了一些细节问题。可从前面博文看到。现完成了编辑博文功能,具体实现过程看下文。
遵循先前端,再后端,最后调整前端的开发流程。
首先,用UEditor编辑博文需要一个页面。该页面和前面的新增博文页面类似。

可参考中用UEditor新增博文新增博文页面。
不过这里要考虑怎么获取博文的内容,并写入到UEditor中。虽然可以直接把内容写到模版页面中,但显得页面臃肿。这里可以用ajax在加载UEditor的时候,请求博文内容并写入进去。
由于这里的相关链接还未确定,先使用前面新增博客使用到的页面。大致的页面代码如下:
{% extends "blog/index.html" %}
{% block blog_head %}
<script type="text/javascript" src="/ueditor/UE/ueditor.config.js"></script>
<script type="text/javascript" src="/ueditor/UE/ueditor.all.min.js"></script>
<script type="text/javascript" src="/ueditor/UE/lang/zh-cn/zh-cn.js"></script>
<script type="text/javascript">
var ue = UE.getEditor('editor');
</script>
{% endblock %}
{% block blog_content%}
<form id="blog_form" action="" methord="POST">
{% csrf_token %}
<div class="input-group">
<span id="blog_title_label" class="input-group-addon">博文标题</span>
<input id="blog_title" name="blog_title" type="text" class="form-control"
placeholder="请写入不超过50个字符的标题"
/>
</div>
<div class="input-group">
<span id="tag-list-label" class="input-group-addon">分类标签</span>
<div class="tag-list">
<ul style="overflow: hidden;">
<li class="tag-recommend">
<input id="recommend" type="checkbox" name="recommend" />
<label for="recommend">推荐</label>
</li>
{% for tag in tags %}
<li>
<input id="tag_{{tag.id}}" type="checkbox" name="tag" value="{{tag.id}}" />
<label for="tag_{{tag.id}}">{{tag.tag_name}}</label>
</li>
{% endfor %}
</ul>
</div>
<div class="clearfix"></div>
</div>
<!--用于提交UEditor内容-->
<input id="content" name="content" type="hidden">
</form>
<div style="min-height:600px">
<script id="editor" type="text/plain" style="width:100%;height:500px;"></script>
</div>
<div style="text-align:right;">
<span class="tip-text"></span>
<a id="blog_submit" class="btn btn-primary" href="javascript:void(0);">发表博客</a>
</div>
{% endblock %}此处要注意:
1)UEditor的加载,看第9行代码;
2)form表单有3个字段:博客标题(blog_title)、分类标签(blog_tags)、正文内容(content);
3)为了方便提交数据,正文内容字段是一个hidden类型的input标签。提交数据之前,先把UEditor的内容赋值给这个标签;
4)分类标签,即代码中的tags。我采用复选框的形式。该这段是多对多ManyToMany类型,后面处理有些细节会和大家讲解;
5)因为是POST提交(POST提交可以提交很多数据),需要加上{% csrf_token %}。
保存这个文件名为blog_edit.html。其他的相关js代码还没写,后面逐步完善。
接着写后端的代码,这里需要3个函数:
def edit(request, blog_id): #打开编辑页面 pass def edit_content(request, blog_id): #获取博文内容 pass def edit_ajax(requese, blog_id): #修改结果提交 pass
先定好框架,也为了方便讲解和理解。同时urls.py添加这3个处理方法的请求链接:
from blog import views as blog_views urlpatterns = [ #其他无关的链接隐藏不显示 url(r'^blog_edit/(?P<blog_id>\d+)$', blog_views.edit, name='blog_edit'), url(r'^blog_edit_content/(?P<blog_id>\d+)$', blog_views.edit_content, name='blog_edit_content'), url(r'^blog_edit_ajax/(?P<blog_id>\d+)$', blog_views.edit_ajax, name='blog_edit_ajax'), ]
urls设置中使用了参数。该参数名要和views.py里面处理相应的处理方法对应参数名字相同。
这里还需要再写个装饰器,判断是否进入编辑博文页面的人是否是该博文的作者。
如果谁都可以进入编辑,那就乱套了。装饰器如下:
#判断对应博文是否当期登录用户发表的
def check_blog_author(func):
def warpper(request, blog_id):
try:
#判断是否有登录
if not request.user.is_authenticated():
raise Exception(u'您尚未登录!')
#判断博文是否存在
blogs = Blog.objects.filter(id=blog_id)
if blogs.count == 0:
raise Exception(u'找不到没有对应的博客')
#判断发表博客的作者和当前登录的用户是否一致
blog = blogs[0]
if blog.author != request.user:
raise Exception(u'您不是该博文的作者,不能编辑')
#验证通过,继续执行
return func(request, blog_id)
except Exception as e:
data = {}
data['goto_url'] = '/'
data['goto_time'] = 3000
data['goto_page'] = True
data['message'] = e.message
return render_to_response('message.html',data)
return warpper由于我的用户系统和博文的作者都是使用Django自带的用户管理系统,可以直接用request.user判断。另外,这里的blog_id参数和那3个编辑处理方法对应。名字也必须一样,因为urls.py设置是这个名字。
写完装饰器就可以写打开编辑博文页面的方法。此处需要把相关博文信息传递过去,例如标题、分类标签等。代码如下:
@check_blog_author(False)
def edit(request, blog_id):
blog = Blog.objects.get(id=blog_id)
#处理tag分类标签
tags = []
blog_tags = blog.tags.all()
for tag in Tag.objects.all():
#给分类标签添加临时属性,表示是否选中该类
tag.checked = True if tag in blog_tags else False
tags.append(tag)
data = {}
data['blog'] = blog
data['tags'] = tags
return render_to_response("blog/blog_edit.html", data, context_instance=RequestContext(request))这个tag是我blog应用的模型,可参考我前面的博文。Blog和Tag是多对多关系,可用blog.tags.all()获取该博文使用了哪些分类标签。
修改前端页面,才能看到实际效果:
{% extends "blog/index.html" %}
{% block blog_head %}
<script type="text/javascript" src="/ueditor/UE/ueditor.config.js"></script>
<script type="text/javascript" src="/ueditor/UE/ueditor.all.min.js"></script>
<script type="text/javascript" src="/ueditor/UE/lang/zh-cn/zh-cn.js"></script>
<script type="text/javascript">
var ue = UE.getEditor('editor');
</script>
{% endblock %}
{% block blog_content%}
<form id="blog_form" action="" methord="POST">
{% csrf_token %}
<div class="input-group">
<span id="blog_title_label" class="input-group-addon">博文标题</span>
<input id="blog_title" name="blog_title" type="text" class="form-control"
placeholder="请写入不超过50个字符的标题"
value="{{blog.caption}}"
/>
</div>
<div class="input-group">
<span id="tag-list-label" class="input-group-addon">分类标签</span>
<div class="tag-list">
<ul style="overflow: hidden;">
<li class="tag-recommend">
<input id="recommend" type="checkbox" name="recommend"
{%if blog.recommend%}checked{%endif%}
/>
<label for="recommend">推荐</label>
</li>
{% for tag in tags %}
<li>
<input id="tag_{{tag.id}}" type="checkbox" name="tag"
value="{{tag.id}}"
{%if tag.checked%}checked{%endif%}
/>
<label for="tag_{{tag.id}}">{{tag.tag_name}}</label>
</li>
{% endfor %}
</ul>
</div>
<div class="clearfix"></div>
</div>
<!--用于提交UEditor内容-->
<input id="content" name="content" type="hidden">
</form>
<div style="min-height:600px">
<script id="editor" type="text/plain" style="width:100%;height:500px;"></script>
</div>
<div style="text-align:right;">
<span class="tip-text"></span>
<a id="blog_submit" class="btn btn-primary" href="javascript:void(0);">发表博客</a>
</div>
{% endblock %}若要让input复选框勾选上的话,只需加入一个checked属性即可。
获取基本信息之后,再写个返回博文内容的请求方法。同样该方法也要验证一下身份,若不验证身份的话,容易被其他人当作接口直接抓取博文内容。
这里判断的代码和刚刚写的装饰器一样。若还使用前面的装饰器有问题,因为前面写的装饰器若验证不通过的话,会跳转到一个消息页面。这个行为就不符合我们要求的json数据。
若重新写一个一样的代码,只是返回的数据不同的装饰器又比较冗余。
可以考虑给这个装饰器加一个参数。判断返回数据的类型:
#判断对应博文是否当期登录用户发表的
def check_blog_author(is_json = False):
def __check_blog_author(func):
def warpper(request, blog_id):
try:
#判断是否有登录
if not request.user.is_authenticated():
raise Exception(u'您尚未登录!')
#判断博文是否存在
blogs = Blog.objects.filter(id=blog_id)
if blogs.count == 0:
raise Exception(u'找不到没有对应的博客')
#判断发表博客的作者和当前登录的用户是否一致
blog = blogs[0]
if blog.author != request.user:
raise Exception(u'您不是该博文的作者,不能编辑')
#验证通过,继续执行
return func(request, blog_id)
except Exception as e:
data = {}
#判断是否是要返回json
if is_json:
data['success'] = False
data['message'] = e.message
return HttpResponse(json.dumps(data), content_type = 'application/json')
else:
data['goto_url'] = '/'
data['goto_time'] = 3000
data['goto_page'] = True
data['message'] = e.message
return render_to_response('message.html',data)
return warpper
return __check_blog_author带参数的装饰器就复杂一些。给这个装饰器的参数写默认值,就可以不用修改之前的方法。
获取博文内容的方法,代码如下:
@check_blog_author(True)
def edit_content(request, blog_id):
blog = Blog.objects.get(id=blog_id)
data = {}
data['success'] = True
data['message'] = blog.content
return HttpResponse(json.dumps(data), content_type = 'application/json')很少代码即可返回对应的数据。其中,该json有两个参数。1个是success表示是否成功获取到数据;1个是message是保存需要返回的数据。若成功获取,就返回博文内容;若获取不成功,就返回错误信息。
更新前端代码。这里需要更新UEditor初始化部分的代码即可:
<script type="text/javascript">
//编辑器初始化完成再赋值
var ue = UE.getEditor('editor');
ue.ready(function(){
$.get("{%url 'blog_edit_content' blog.id%}", {}, function(data){
if(data['success']){
//成功则加载博文内容
var content = data['message'];
ue.setContent(content);
}else{
//出错则显示错误信息
$('.tip-text').text(data['message']);
}
}
);
});
</script>到此位置,获取博文的内容完成了。
接下来,只需把博文修改提交即可。此处我还是使用ajax提交数据。为了避免混乱,获取数据和验证数据包装成一个方法。之前写的新增博客获取和验证数据也可使用该方法:
#检查和获取博客新增修改POST提交的数据
def _ajax_post_data(request):
if request.method != 'POST':
raise Exception('method error')
#获取数据
blog_title = request.POST.get('blog_title')
blog_tags = request.POST.getlist('tag')
blog_recommend = request.POST.get('recommend', False)
blog_content = request.POST.get('content')
#处理tags
tag_ids = []
for tag in blog_tags:
try:
tag_id = int(tag)
tag_ids.append(tag_id)
except:
pass
#验证数据
if len(blog_title) <=0 or len(blog_title)>=50:
raise Exception(u'请输入一个不超过50个字符的标题')
if len(tag_ids)<1:
raise Exception(u'请选择除了“推荐”之外的至少1个类别')
if len(blog_content)==0:
raise Exception(u'尚未写入任何博文内容')
#返回数据
post_data = {}
post_data['blog_title'] = blog_title
post_data['tag_ids'] = tag_ids
post_data['blog_recommend'] = blog_recommend
post_data['blog_content'] = blog_content
return post_data若验证通过,返回一个包含所需数据的字典。
具体修改博文的方法如下:
@check_blog_author(True)
def edit_ajax(request, blog_id):
data = {}
try:
#检查和获取博客修改POST提交的数据
post_data = _ajax_post_data(request)
#修改博文
blog = Blog.objects.get(id=blog_id)
blog.caption = post_data['blog_title']
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)
#返回结果
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')这里需要注意tags的处理即可,先清空,再添加。
写完后台代码,调整前端页面。加上ajax提交代码:
<script type="text/javascript">
$("#blog_submit").click(function(){
//检查数据
if(!check_data()) return false;
$('.tip-text').text('修改博客中...');
$("#blog_submit").addClass('disabled');
//把UEditor内容写入hidden中
var ue = UE.getEditor('editor');
$('#content').val(ue.getContent());
//ajax提交表单
$.ajax({
type: "POST",
data: $('#blog_form').serialize(),
url: "{% url 'blog_edit_ajax' blog.id%}",
cache: false,
dataType: "json",
success: function(json, textStatus) {
if(json['success']){
//跳转页面
$('.tip-text').text('修改博文成功,跳转页面中...');
var url = json['message'];
window.location.href = url;
}else{
//显示错误信息
$('.tip-text').text('修改博客出错:'+json['message']);
}
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
$('.tip-text').text('修改博客出错,请检查或稍后再试');
}
});
$("#blog_submit").removeClass('disabled');
});
function check_data(){
$('.tip-text').text('');
//标题
var title = $('#blog_title').val();
if(title.length<=0 || title.lenght>=50){
$('.tip-text').text('请输入一个不超过50个字符的标题');
return false;
}
//类别
if($('input[name=tag]:checked').length==0){
$('.tip-text').text('请选择除了“推荐”之外的至少1个分类');
return false;
}
//内容
if (!UE.getEditor('editor').hasContents()){
$('.tip-text').text('尚未写入任何博文内容');
return false;
}
return true;
}
</script>这里的代码包含前端验证数据和ajax提交数据。基本和之前的新增博文代码一样。
最后,在博文显示页面添加入口链接即可。
<a href="{%url 'blog_edit' blog.id%}">编辑</a>
若你只是想让作者看到该链接,其他人看不到。需要在响应打开博文页面的方法,添加如下代码:
#是否显示编辑链接 show_edit_url = False if request.user.is_authenticated(): if request.user == blog.author: show_edit_url = True
再将该变量传递给模版页面,模版页面用if判断即可。