博客项目(二)
版权声明:本博客来自路飞学城Python全栈开发培训课件,仅用于学习之用,严禁用于商业用途。
欢迎访问路飞学城官网:https://www.luffycity.com/
本节实现文章的后台管理,包括文章、文章分类、文章标签的增删改查功能。
1. 补充知识点-富文本框的使用
KindEditor 是一套开源的在线HTML编辑器,主要用于让用户在网站上获得所见即所得编辑效果,开发人员可以用 KindEditor 把传统的多行文本输入框(textarea)替换为可视化的富文本输入框。 KindEditor 使用 JavaScript 编写,可以无缝地与 Java、.NET、PHP、ASP 等程序集成,比较适合在 CMS、商城、论坛、博客、Wiki、电子邮件等互联网应用上使用。
为了能够更好地在后台编辑文章,我们会在项目中引入富文本编辑器中的其中一款kindeditor。
html页面:
<script src="/static/js/jquery-3.2.1.min.js"></script>
<script charset="utf-8" src="/static/blog/kindeditor/kindeditor-all.js"></script>
<script>
KindEditor.ready(function(K) {
window.editor = K.create('#article_content',{
width:"800",
height:"600",
resizeType:0,
uploadJson:"/upload/",
extraFileUploadParams:{
csrfmiddlewaretoken:$("[name='csrfmiddlewaretoken']").val()
},
filePostName:"upload_img"
});
});
</script>
url路由配置:
# 文本编辑器上传图片url
path('upload/', views.upload),
图像上传的视图函数:
def upload(request):
"""
编辑器上传文件接受视图函数
:param request:
:return:
"""
print(request.FILES)
img_obj=request.FILES.get("upload_img")
print(img_obj.name)
path=os.path.join(settings.MEDIA_ROOT,"add_article_img",img_obj.name)
with open(path,"wb") as f:
for line in img_obj:
f.write(line)
return HttpResponse("ok")
2. 文章、分类、标签增删改查
创建From模型
# MyForms.py
class ArticleForm(forms.Form):
title = forms.CharField(max_length=32,
min_length=4,
label="标题",
error_messages={"min_length": "标题太短了,至少4个字符",
"required": "该字段不能为空!"},
widget=forms.widgets.TextInput(attrs={'class': 'form-control'}
))
content = forms.CharField(min_length=4,
required=False,
label="内容(Kindeditor编辑器,不支持拖放/粘贴上传图片)",
error_messages={"min_length": "你太短了"},
widget=forms.widgets.Textarea(attrs={'class': 'form-control'}
))
category = forms.ChoiceField(label="文章分类", error_messages={"required": "该字段不能为空"},
widget=forms.widgets.Select(attrs={'class': 'form-control'}))
tags = forms.MultipleChoiceField(label="标签", error_messages={"required": "该字段不能为空"},
widget=forms.widgets.SelectMultiple(attrs={'class': 'form-control'}))
def __init__(self, *args, **kwargs):
user = kwargs.pop("user")
blog = user.blog
super(ArticleForm, self).__init__(*args, **kwargs)
self.fields['category'].choices = models.Category.objects.filter(blog=blog).values_list('pk', 'title')
self.fields['tags'].choices = models.Tag.objects.filter(blog=blog).values_list('pk', 'title')
小技巧:
因为不同用户都有各自的文章分类和标签,所以在初始化文章表单的时候需要根据用户来筛选对应的的记录。此时通过视图函数传输一个user对象,实现对分类和标签的筛选。
配置路由
urlpatterns = [
...
# 后台管理url
re_path("cn_backend/$",views.cn_backend),
re_path("cn_backend/add_article/$",views.add_article),
re_path("^cn_backend/edit_article/(?P<article_id>\d+)$",views.edit_article),
re_path("^cn_backend/del_article/(?P<article_id>\d+)$",views.del_article),
re_path("cn_backend/category/$",views.category),
re_path("cn_backend/del_category/(?P<category_id>\d+)$",views.del_category),
re_path("cn_backend/tags/$",views.tags),
re_path("^cn_backend/edit_tag/$",views.edit_tag),
re_path("cn_backend/del_tag/(?P<tag_id>\d+)$",views.del_tag),
# 后台上传图片
path("upload/", views.upload),
...
创建视图函数
# views.py
...
def page_util(request, obj_list, num_of_page=3):
"""
分页函数,单独处理分页逻辑
:param request: 页面请求对象
:param obj_list: 要分页的数据源
:param num_of_page: 每页要显示的条数
:return:
"""
paginator = Paginator(obj_list, num_of_page)
page = request.GET.get('page', 1)
currentPage = int(page)
if paginator.num_pages > 7:
# 如果大于7,分情况处理
if currentPage - 3 < 1:
pageRange = range(1, 8)
elif currentPage + 3 > paginator.num_pages:
pageRange = range(currentPage - 6, paginator.num_pages + 1) # 左开右闭
else:
pageRange = range(currentPage - 3, currentPage + 4)
else:
pageRange = paginator.page_range
try:
obj_list = paginator.page(page)
except PageNotAnInteger:
obj_list = paginator.page(1)
except EmptyPage:
obj_list = paginator.page(paginator.num_pages)
return obj_list, paginator, currentPage, pageRange
@login_required
def cn_backend(request):
user_obj = request.user
username = user_obj.username
blog = user_obj.blog
article_list = models.Article.objects.filter(user=user_obj)
# 调用分页函数
article_list, paginator, currentPage, pageRange = page_util(request, article_list, 5)
return render(request, "backend/backend.html", locals())
@login_required
def add_article(request):
user = request.user
if request.method == "GET":
form_obj = MyForms.ArticleForm(user=user)
return render(request, "backend/add_article.html", {"form": form_obj})
# 给form表单传入user参数,用来在初始化文章分类和标签时筛选出该用户名下的分类和标签
form_obj = MyForms.ArticleForm(request.POST, user=user)
if form_obj.is_valid():
title = form_obj.cleaned_data.get("title")
content = form_obj.cleaned_data.get("content")
category_id = form_obj.cleaned_data.get("category")
tag_id_list = form_obj.cleaned_data.get("tags")
# 防止xss攻击,过滤script标签
soup = BeautifulSoup(content, "html.parser")
for tag in soup.find_all():
if tag.name == "script":
tag.decompose()
# 构建摘要数据,获取标签字符串的文本前150个符号
desc = soup.text[0:150] + "..."
article_obj = models.Article.objects.create(title=title, desc=desc, content=str(soup),
category_id=category_id, user=user)
# 绑定标签,因为是自建关系表,不能通过关联管理器操作。
for tag_id in tag_id_list:
models.Article2Tag.objects.create(article=article_obj, tag_id=tag_id)
return redirect("/cn_backend/")
else:
return render(request, "backend/add_article.html", {"form": form_obj})
@login_required
def edit_article(request, article_id):
article_obj = models.Article.objects.filter(pk=article_id).first()
tag_id_list = list(article_obj.tags.all().values_list("pk"))
# 格式转化:将[(1,), (2,)]转化为[1, 2]
tag_id_list = [l[0] for l in tag_id_list]
user = request.user
if request.method == "GET":
form_obj = MyForms.ArticleForm(
user=user,
initial={
'title': article_obj.title,
'content': article_obj.content,
'category': article_obj.category.pk,
'tags': tag_id_list,
},
)
return render(request, "backend/edit_article.html", {"form": form_obj})
form_obj = MyForms.ArticleForm(request.POST, user=user)
if form_obj.is_valid():
title = form_obj.cleaned_data.get("title")
content = form_obj.cleaned_data.get("content")
category_id = form_obj.cleaned_data.get("category")
tag_id_list = form_obj.cleaned_data.get("tags")
# 防止xss攻击,过滤script标签
soup = BeautifulSoup(content, "html.parser")
for tag in soup.find_all():
if tag.name == "script":
tag.decompose()
# 构建摘要数据,获取标签字符串的文本前150个符号
desc = soup.text[0:150] + "..."
article_obj.title = title
article_obj.desc = desc
article_obj.content = str(soup)
article_obj.category_id = category_id
article_obj.user = user
article_obj.save()
# 绑定标签
# 先清空原来绑定关系
models.Article2Tag.objects.filter(article=article_obj).delete()
# 再重新绑定新的关系
for tag_id in tag_id_list:
models.Article2Tag.objects.create(article=article_obj, tag_id=tag_id)
return redirect("/cn_backend/")
else:
return render(request, "backend/eidt_article.html", {"form": form_obj})
@login_required
def del_article(request, article_id):
models.Article.objects.filter(pk=article_id).delete()
return redirect("/cn_backend/")
@login_required
def category(request):
user_obj = request.user
blog = user_obj.blog
if request.method == "GET":
# 查询当前站点的每一个分类名称以及对应的文章数
cate_list = models.Category.objects.filter(blog=blog).values("pk").annotate(
c=Count("article__title")).values_list(
"title", "c", "pk")
return render(request, "backend/edit_cate.html", {"cate_list": cate_list})
response = {"flag": None, "msg": None}
type = request.POST.get("type")
title = request.POST.get("title")
cate_pk = request.POST.get("cate_pk") # 仅在更新时有值
cate_obj = models.Category.objects.filter(title=title).first()
if cate_obj:
response["flag"] = False
response["msg"] = "该分类已存在!"
else:
if type == "add":
cate_new_obj = models.Category.objects.create(title=title, blog=blog)
response["msg"] = "添加成功!"
else:
cate_new_obj = models.Category.objects.filter(pk=cate_pk).first()
cate_new_obj.title = title
cate_new_obj.blog = blog
cate_new_obj.save()
response["msg"] = "更新成功!"
response["flag"] = True
response["cate_pk"] = cate_new_obj.pk
return JsonResponse(response)
@login_required
def del_category(request, category_id):
models.Category.objects.filter(pk=category_id).delete()
return redirect("/cn_backend/category/")
@login_required
def tags(request):
user_obj = request.user
blog = user_obj.blog
# 查询当前站点的每一个分类名称以及对应的文章数
tag_list = models.Tag.objects.filter(blog=blog).values("pk").annotate(
c=Count("article__title")).values_list("title", "c", "pk")
return render(request, "backend/tags.html", {"tag_list": tag_list})
@login_required
def edit_tag(request):
user_obj = request.user
blog = user_obj.blog
response = {"flag": None, "msg": None}
tag_title = request.POST.get("tag_title")
tag_type = request.POST.get("tag_type")
if request.method == "POST":
tag_new_obj = models.Tag.objects.filter(title=tag_title)
if tag_new_obj:
response["flag"] = False
response["msg"] = "该标签已存在"
else:
if tag_type == "add":
models.Tag.objects.create(title=tag_title, blog=blog)
else:
tag_id = request.POST.get("tag_id")
tag_obj = models.Tag.objects.filter(pk=tag_id).first()
tag_obj.title = tag_title
tag_obj.save()
response["flag"] = True
return JsonResponse(response)
@login_required
def del_tag(request, tag_id):
models.Tag.objects.filter(pk=tag_id).delete()
return redirect("/cn_backend/tags/")
def upload(request):
"""
编辑器上传文件接受视图函数
:param request:
:return:
"""
img_obj = request.FILES.get("upload_img")
path = os.path.join(settings.MEDIA_ROOT, "add_article_img", img_obj.name)
with open(path, "wb") as f:
for line in img_obj:
f.write(line)
response = {
"error": 0,
"url": "media/add_article_img/{}".format(img_obj.name)
}
return HttpResponse(json.dumps(response))
创建模板
base.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>博客后台管理</title>
<link rel="stylesheet" href="/static/blog/bs/css/bootstrap.css">
<script src="/static/js/jquery-3.2.1.min.js"></script>
<script src="/static/blog/bs/js/bootstrap.min.js"></script>
<link rel="stylesheet" href="/static/blog/css/backend.css">
</head>
<body>
<div class="header">
<p class="title">
后台管理
<a class="info" href="/logout/">注销</a>
<a class="info" href="/">返回首页</a>
<a class="info" href="/{{ request.user.username }}/">个人主页</a>
</p>
</div>
<div class="container">
<div class="col-md-2">
<div class="panel-group" id="accordion" role="tablist" aria-multiselectable="true">
<div class="panel panel-default">
<div class="panel-heading" role="tab" id="headingOne">
<h4 class="panel-title">
<a role="button" data-toggle="collapse" data-parent="#accordion" href="#collapseOne"
aria-expanded="true" aria-controls="collapseOne">
操作
</a>
</h4>
</div>
<div id="collapseOne" class="panel-collapse collapse in" role="tabpanel" aria-labelledby="headingOne">
<div class="panel-body">
<p><a href="/cn_backend/add_article/">添加文章</a></p>
<p><a href="/cn_backend/category/">文章分类</a></p>
<p><a href="/cn_backend/tags/">标签管理</a></p>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-9">
<div>
<!-- Nav tabs -->
<ul class="nav nav-tabs" role="tablist">
<li role="presentation" class="active"><a href="#home" aria-controls="home" role="tab"
data-toggle="tab">文章</a></li>
<li role="presentation"><a href="#profile" aria-controls="profile" role="tab"
data-toggle="tab">日记</a></li>
<li role="presentation"><a href="#messages" aria-controls="messages" role="tab" data-toggle="tab">眼镜</a>
</li>
<li role="presentation"><a href="#settings" aria-controls="settings" role="tab" data-toggle="tab">相册</a>
</li>
</ul>
<!-- Tab panes -->
<div class="tab-content">
<div role="tabpanel" class="tab-pane active" id="home">
{% block content %}
{% endblock %}
</div>
<div role="tabpanel" class="tab-pane" id="profile">
<img src="/static/blog/img/meinv2.jpg" alt="">
<img src="/static/blog/img/meinv3.jpg" alt="">
<img class="pull-right" src="/static/blog/img/meinv.jpg" alt="">
</div>
<div role="tabpanel" class="tab-pane" id="messages">
<img width="180" height="180" src="/static/blog/img/hashiqi2.jpg" alt="">
<img width="180" height="180" src="/static/blog/img/dogg4.jpg" alt="">
<img width="180" height="180" src="/static/blog/img/linhaifeng.jpg" alt=""><br>
<img width="180" height="180" src="/static/blog/img/dogg3.jpeg" alt="">
<img width="180" height="180" src="/static/blog/img/dogge2.jpg" alt="">
<img width="180" height="180" src="/static/blog/img/dogg5.jpg" alt="">
</div>
<div role="tabpanel" class="tab-pane" id="settings">
</div>
</div>
</div>
</div>
</div>
</body>
</html>
backend.html:
{% extends "backend/base.html" %}
{% block content %}
<div class="article_list small">
<table class="table table-hover table-striped">
<thead>
<tr>
<th>标题</th>
<th>文章分类</th>
<th>评论数</th>
<th>点赞数</th>
<th>操作</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{% for article in article_list %}
<tr>
<td><a href="/{{ request.user.username }}/articles/{{ article.pk }}">
{{ article.title }}
</a>
</td>
<td>{{ article.category }}</td>
<td>{{ article.comment_count }}</td>
<td>{{ article.up_count }}</td>
<td><a class="text-primary" href="/cn_backend/edit_article/{{ article.pk }}">
编辑</a>
</td>
<td><a class="text-danger" href="/cn_backend/del_article/{{ article.pk }}">删除</a></td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="text-center">
<ul class="pagination" id="pager">
{% if article_list.has_previous %}
<li class="previous">
<a href="{{ current_path }}?page={{ article_list.previous_page_number }}">上一页</a>
</li>
{% else %}
<li class="previous disabled"><a href="#">上一页</a></li>
{% endif %}
{% for num in pageRange %}
{% if num == currentPage %}
<li class="item active">
<a href="{{ current_path }}?page={{ num }}">{{ num }}</a>
</li>
{% else %}
<li class="item"><a href="{{ current_path }}?page={{ num }}">{{ num }}</a></li>
{% endif %}
{% endfor %}
{% if article_list.has_next %}
<li class="next">
<a href="{{ current_path }}?page={{ article_list.next_page_number }}">下一页</a>
</li>
{% else %}
<li class="next disabled"><a href="#">下一页</a></li>
{% endif %}
</ul>
</div>
</div>
{% endblock %}
add_article.html:
{% extends "backend/base.html" %}
{% block content %}
<form action="" method="post">
{% csrf_token %}
<div class="add_article">
<div class="cnb-panel-header">添加文章</div>
<div class="add_article_region">
<div class="title form-group">
<label for="id_{{ form.title.name }}">{{ form.title.label }}</label>
{{ form.title }}<span class="pull-right error">{{ form.title.errors.0 }}</span>
</div>
<div class="content form-group">
<label for="article_content">{{ form.content.label }}</label>
{{ form.content }}
<span class="pull-right error">{{ form.content.errors.0 }}</span>
</div>
<div class="title form-group">
<label for="id_{{ form.category.name }}">{{ form.category.label }}</label>
{{ form.category }}<span class="pull-right error">{{ form.category.errors.0 }}</span>
</div>
<div class="title form-group">
<label for="id_{{ form.tags.name }}">{{ form.tags.label }}</label>
{{ form.tags }}<span class="pull-right error">{{ form.tags.errors.0 }}</span>
</div>
<input type="submit" class="btn btn-default">
</div>
</div>
</form>
<script charset="utf-8" src="/static/blog/kindeditor/kindeditor-all.js"></script>
<script>
KindEditor.ready(function (K) {
window.editor = K.create('#id_content', {
width: "800",
height: "300",
resizeType: 0,
uploadJson: "/upload/",
extraFileUploadParams: {
csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val()
},
filePostName: "upload_img"
});
});
</script>
{% endblock %}
edit_article.html:
{% extends "backend/base.html" %}
{% block content %}
<form action="" method="post">
{% csrf_token %}
<div class="add_article">
<div class="alert-success text-center">编辑文章</div>
<div class="add_article_region">
<div class="title form-group">
<label for="id_{{ form.title.name }}">{{ form.title.label }}</label>
{{ form.title }}<span class="pull-right error">{{ form.title.errors.0 }}</span>
</div>
<div class="content form-group">
<label for="id_content">{{ form.content.label }}</label>
{{ form.content }}
<span class="pull-right error">{{ form.content.errors.0 }}</span>
</div>
<div class="title form-group">
<label for="id_{{ form.category.name }}">{{ form.category.label }}</label>
{{ form.category }}<span class="pull-right error">{{ form.category.errors.0 }}</span>
</div>
<div class="title form-group">
<label for="id_{{ form.tags.name }}">{{ form.tags.label }}</label>
{{ form.tags }}<span class="pull-right error">{{ form.tags.errors.0 }}</span>
</div>
<input type="submit" class="btn btn-default">
</div>
</div>
</form>
<script charset="utf-8" src="/static/blog/kindeditor/kindeditor-all.js"></script>
<script>
KindEditor.ready(function (K) {
window.editor = K.create('#id_content', {
width: "800",
height: "300",
resizeType: 0,
uploadJson: "/upload/",
extraFileUploadParams: {
csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val()
},
filePostName: "upload_img"
});
});
</script>
{% endblock %}
edit_cate.html:
{% extends "backend/base.html" %}
{% block content %}
<div class="cnb-panel-header">
<span>编辑分类</span>
</div>
<div class="category_list small">
<table class="table table-hover table-striped" id="cate_table">
<thead>
<th>文章分类</th>
<th>文章数</th>
<th>操作</th>
<th>操作</th>
</thead>
<tbody>
{% for cate in cate_list %}
<tr>
<td>{{ cate.0 }}</td>
<td>{{ cate.1 }}</td>
<td><a class="text-primary edit_cate" index="{{ cate.2 }}" >编辑</a></td>
<td><a class="text-danger" href="javascript:void(0)"
onclick="showDeleteModal(this)" index="{{ cate.2 }}">删除</a></td>
</tr>
{% endfor %}
</tbody>
</table>
<!-- 模态框 信息删除确认 -->
<div class="modal fade" id="delcfmOverhaul">
<div class="modal-dialog">
<div class="modal-content message_align">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"
aria-label="Close">
<span aria-hidden="true">×</span>
</button>
<h4 class="modal-title">提示</h4>
</div>
<div class="modal-body">
<!-- 隐藏需要删除的id -->
<input type="hidden" id="deleteHaulId"/>
<p>该分类下有<span id="article_count"></span>篇文章,</p>
<p>您确认要删除吗?</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default"
data-dismiss="modal">取消
</button>
<button type="button" class="btn btn-primary"
id="deleteHaulBtn">确认
</button>
</div>
</div>
<!-- /.modal-content -->
</div>
<!-- /.modal-dialog -->
</div>
</div>
<hr>
<div class="cnb-panel-header">
<span id="cate_head">新增分类</span>
</div>
<form>
{% csrf_token %}
<div class="form-group">
<label for="cate_tile">分类名称</label>
<input type="text" name="cate_title" id="cate_tile" class="form-control">
<span class="error" id="cate_error"></span>
</div>
<input type="button" class="btn btn-default" id="cate_btn" value="提交">
</form>
<script>
var cate_pk = "";
var cate_type = "add";
$("#cate_tile").focus(function () {
$("#cate_error").html("");
})
$("#cate_btn").click(function () {
var cate_title = $("#cate_tile").val()
console.log(cate_title.length);
if (cate_title) {
if (cate_title.length > 16) {
$("#cate_error").html("名称太长了!");
} else {
$.ajax({
url: "/cn_backend/category/",
type: "POST",
data: {
"type": cate_type,
"title": cate_title,
"cate_pk": cate_pk,
"csrfmiddlewaretoken": $("[name = 'csrfmiddlewaretoken']").val(),
},
success: function (data) {
if (data.flag) {
if (cate_type === "add") {
// 异步添加记录到表格中
var tr_s = `
<tr>
<td>${cate_title}</td>
<td>0</td>
<td><a href="/cn_backend/edit_category/${data.cate_pk}">
编辑
</a>
</td>
<td><a href="/cn_backend/del_category/${data.cate_pk} ">删除
</a>
</td>
</tr>`
$("#cate_table>tbody").append(tr_s);
//清空输入框
$("#cate_tile").html("");
cate_pk = "";
cate_type = "add";
} else {
location.reload();
}
} else {
$("#cate_error").html("该分类已存在!");
}
}
})
}
} else {
$("#cate_error").html("文章分类不能为空!");
}
})
$(".edit_cate").click(function () {
cate_pk = $(this).attr("index");
cate_type = "update";
var cate_title = $(this).parent().parent().find("td").eq(0).text();
$("#cate_head").html("编辑分类");
$("#cate_tile").val(cate_title);
})
// 打开询问是否删除的模态框并设置需要删除的ID
function showDeleteModal(obj) {
var $tds = $(obj).parent().parent().children();// 获取到所有列
console.log($($tds[1]).text());
var article_count = $($tds[1]).text();// 获取文章数量
var delete_id = $($tds[3]).children("a").attr("index");// 获取隐藏的ID
console.log(delete_id);
$("#deleteHaulId").val(delete_id);// 将模态框中需要删除的ID设为需要删除的ID
$("#article_count").text(article_count);
$("#delcfmOverhaul").modal({
backdrop: 'static',
keyboard: false
});
}
function deleteHaulinfo() {
var del_id = $("#deleteHaulId").val();
$.ajax({
url: "cn_backend/del_category/" + del_id,
type: "GET",
success: function () {
location.reload();
}
})
}
// 删除模态框的确定按钮的点击事件
$("#deleteHaulBtn").click(function () {
// ajax异步删除
deleteHaulinfo();
});
</script>
{% endblock %}
tags.html:
{% extends "backend/base.html" %}
{% block content %}
<div class="cnb-panel-header">
<span>标签管理</span>
</div>
<div>
<button class="btn btn-primary pull-right" id="add_tag" onclick="showAddModal(this)">新增标签 </button>
</div>
<div class="tag_list small">
<table class="table table-hover table-striped" id="tab_table">
<thead>
<th>标签</th>
<th>文章数</th>
<th>操作</th>
<th>操作</th>
</thead>
<tbody>
{% for tag in tag_list %}
<tr>
<td>{{ tag.0 }}</td>
<td>{{ tag.1 }}</td>
<td><a class="text-primary edit_cate" href="javascript:void(0)"
onclick="showEditModal(this)" index="{{ tag.2 }}" >编辑</a></td>
<td><a class="text-danger" href="/cn_backend/del_tag/{{ tag.2 }}" index="{{ tag.2 }}"> 删除</a>
</td>
</tr>
{% endfor %}
</tbody>
<!-- 模态框 -->
<div class="modal fade" id="editTagModal">
<div class="modal-dialog">
<div class="modal-content message_align">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"
aria-label="Close">
<span aria-hidden="true">×</span>
</button>
<h4 class="modal-title" >编辑标签</h4>
</div>
<div class="modal-body">
<form >
{% csrf_token %}
<div class="form-group">
<label for="tag_title">标签名称</label>
<input type="text" name="tag_title" id="tag_title" class="form-control">
<span class="error" id="cate_error"></span>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default"
data-dismiss="modal">取消
</button>
<button type="button" class="btn btn-primary"
id="editBtn">确认
</button>
</div>
</div>
<!-- /.modal-content -->
</div>
<!-- /.modal-dialog -->
</div>
</table>
</div>
<hr>
<script>
var tag_id = "";
var tag_title = "";
var tag_type ="";
// 打开编辑模态框
function showEditModal(obj) {
$(".modal-title").html("编辑标签");
tag_type = "edit";
var $tds = $(obj).parent().parent().children();// 获取到所有列
tag_title = $($tds[0]).text();// 获取标签名称
tag_id = $($tds[2]).children("a").attr("index");// 获取隐藏的ID
$("#tag_title").val(tag_title);
$("#editTagModal").modal({
backdrop : 'static',
keyboard : false
});
};
// 打开新增模态框
function showAddModal(obj) {
$(".modal-title").html("新增标签");
tag_type = "add";
$("#editTagModal").modal({
backdrop : 'static',
keyboard : false
});
};
function editinfo() {
$.ajax({
url:"/cn_backend/edit_tag/",
type:"post",
data:{
"tag_id":tag_id,
"tag_type":tag_type,
"tag_title":$("#tag_title").val(),
"csrfmiddlewaretoken": $("[name = 'csrfmiddlewaretoken']").val(),
},
success:function (data) {
if(data.flag){
location.reload();
}else{
var myVar;
clearTimeout(myVar);
$("#cate_error").text(data.msg);
myVar= setTimeout(function () {
$("#cate_error").text("");
},2000)
}
}
})
};
$(function () {
// 删除模态框的确定按钮的点击事件
$("#editBtn").click(function() {
// ajax异步编辑
editinfo();
});
})
</script>
{% endblock %}
小技巧:
使用同一个模态框,通过修改模态框title实现新增和更新功能。
效果展示
后台管理主页:
新增文章页:
修改文章页:
文章分类页:
标签管理页:
未完待续
学python,找路飞!更多精彩,尽在路飞学城