大家好,这是皮爷给大家带来的最新的学习Python能干啥?之Django教程,从零开始,到最后成功部署上线的项目。这一节,我们来开发文章的管理功能,包括发布,查看,修改和删除。
Peekpa.com的官方地址:http://peekpa.com
上一节我们开发了Category和Tag的管理,这一节我们就开发一下文章的管理。
首先是在sidebar引入文章管理的两个页面:发布文章和管理页面。同样,我们还要在cms目录下,创建一个post目录,在里面同样有个manage.html和publish.html:
我们的思路还是和之前开发一样的,复用publish.html页面,即发布和修改是同一个页面,通过是否传入文章内容来确认功能。
关于文章的模型,我们在第13节详细的说过,这里就简单的来看一下Post的model有哪些东西,下面的代码在app/post目录下的models.py文件里:
class Post(models.Model):
title = models.CharField(max_length=200)
author = models.ForeignKey('peekpauser.User', on_delete=models.SET_NULL, null=True)
description = models.CharField(max_length=200)
thumbnail = models.URLField()
content = models.TextField()
content_html = models.TextField(blank=True, editable=False)
is_md = models.BooleanField(default=True)
category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True)
tag = models.ManyToManyField(Tag)
priority = models.IntegerField(default=-1)
is_hot = models.BooleanField(default=False)
is_top = models.BooleanField(default=False)
is_main_page = models.BooleanField(default=False)
status = models.PositiveIntegerField(default=STATUS_DRAFT, choices=STATUS_ITEMS)
publish_time = models.DateTimeField(auto_now_add=True)
publish_time_show = models.DateTimeField(default=datetime.datetime.now)
time_id = models.CharField(blank=True, max_length=30)
read_num = models.PositiveIntegerField(default=0)
文章类里面属性较多,所以,我们在开发文章发布页面的时候,理应把这些属性都应该带进去。
我们通过Bootstrap来写好文章的发布页面的表格,最终样子应该长下面这样:
可以看到中间有些部分采用的是Droplist,比如Author,Category, Status,最下面的Tag还是一个区域的checkbox。
这里的publish.html页面就会和之前的页面不一样,因为我们在这个页面里面,需要显示以下数据:
这些数据都是从数据库里面读取的。所以,我们的post_publish_view
方法里面,就要读取出来这些数据,然后通过context的形势返回到前端页面。
def post_publish_view(request):
context = {
'list_data_category': Category.objects.all(),
'list_data_tag': Tag.objects.all(),
'list_data_user': User.objects.all(),
'list_data_status': Post.STATUS_ITEMS,
'form': PostForm()
}
return render(request, 'cms/post/publish.html', context=context)
同样,我们还是要使用Post请求来发送整个表单数据, 所以,我们要和Tag还有Category一样,写一个PostView,还需要一个PostForm。但是这里的PostForm有些不一样:
class PostForm(forms.ModelForm, FormMixin):
tag_id = forms.ModelMultipleChoiceField(widget=forms.CheckboxSelectMultiple, queryset=Tag.objects.all())
class Meta:
model = Post
exclude = ('tag',)
这里我们要单独创建一个tag_id
变量,是因为在我们的Post模型中,tag是一个ManyToMany的类型,这里我们需要在表单中,使用ModelMultipleChoiceField类型将前端返回的多选的Tag封装起来,然后再创建Post实例,保存起来。
所以,这个时候,我们的后端更新文章的代码就是这样:
class PostView(View):
def post(self, request):
# 新建提交
if 'submit' in request.POST:
form = PostForm(request.POST)
if form.is_valid():
title = form.cleaned_data.get('title')
description = form.cleaned_data.get('description')
author = form.cleaned_data.get('author')
thumbnail = form.cleaned_data.get('thumbnail')
status = form.cleaned_data.get('status')
content = form.cleaned_data.get('content')
is_md = form.cleaned_data.get('is_md')
category = form.cleaned_data.get('category')
priority = form.cleaned_data.get('priority')
is_hot = form.cleaned_data.get('is_hot')
is_top = form.cleaned_data.get('is_top')
is_main_page = form.cleaned_data.get('is_main_page')
publish_time_show = form.cleaned_data.get('publish_time_show')
time_id = form.cleaned_data.get('time_id')
read_num = form.cleaned_data.get('read_num')
tags = form.cleaned_data.get('tag_id')
instance = Post.objects.create(title=title, description=description, author=author,
thumbnail=thumbnail, status=status, content=content,
is_md=is_md, category=category, priority=priority,
is_hot=is_hot, is_top=is_top, is_main_page=is_main_page,
publish_time_show=publish_time_show, time_id=time_id,read_num=read_num
)
instance.tag.set(tags)
return redirect(reverse("cms:post_publish_view"))
这里只需要注意倒数第二行的instance.tag.set(tags)
,为什么要这么写?是因为,我们从PostForm里面拿到的tags,是一个QuerySet,但是Django的Object在创建的时候,对于ManyToMany的类型,是不能直接创建的,推荐的方法就是创建一个instance,然后调用instance.变量名字.set(内容)
的方法来实现ManyToMany。
这个时候,别忘了在CMS目录下的urls.py文件里面配置PistView的映射:
urlpatterns = [
path("dashboard/post/add", PostView.as_view(), name="post_add"),
]
现在,发布文章的基本功能就配置完成了,我们在前端来实验一下启动我们的服务器,来到发布页面,我们试着把所有的东西都填写进去:
点击发布,页面又跳回到了Publish页面,这个时候,我们在数据库里面看一下刚才发布的那篇文章:
发现是有的。还能看到,当is_md
为true的时候,我们的content会自动转换成html格式的文件。而且数据完全正确,那么新增文章的功能就完成了。
关于文章的查询,我们还是将文章放到Manage.html页面,里面有个table,然后将文章放进去,做法和Category还有Tag类似:
Manage.html大致结构如下:
<section class="content">
<div class="container-fluid pt-4">
<div class="row">
<div class="col-sm-12">
<div class="card">
<div class="card-body">
<div class="row p-2 d-flex justify-content-between">
<p class="h3">Postp>
<div class="float-right">
<a class="btn btn-primary text-right" href="{% url 'cms:post_publish_view' %}"><i class="mr-2 fas fa-plus">i>Adda>
div>
div>
<table class="table table-bordered table-hover">
<thead class="thead-light">
<tr>
<th style="width: 10%;">#th>
<th>Post_Time_IDth>
<th>Post_titleth>
<th>Post_show_timeth>
<th class="w-25">actionsth>
tr>
thead>
<tbody>
{% for item in list_data %}
<tr>
<td>{{ item.id }}td>
<td>{{ item.time_id }}td>
<td>{{ item.title }}td>
<td>{{ item.publish_time_show }}td>
<td>
<a href="{% url 'cms:tag_edit' %}?tag_id={{ item.id }}" class="btn btn-info btn-xs">Modifya>
<button class="btn btn-danger btn-xs delete-btn" data-tag-id="{{ item.id}}">
Delete
button>
td>
tr>
{% endfor %}
tbody>
table>
div>
div>
div>
div>
div>
section>
文件的manage视图函数:
def post_manage_view(request):
context = {
"list_data": Post.objects.all()
}
return render(request, 'cms/post/manage.html', context=context)
最后效果图:
Post的修改,和Category还有Tag的思路是一样的:
所以,视图函数很简单,通过id来取文章:
class PostEditView(View):
def get(self, request):
post_id = request.GET.get('post_id')
post = Post.objects.get(pk=post_id)
tag_list = list()
for item in post.tag.all():
tag_list.append(item.id)
context = {
'item_data': post,
'list_data_category': Category.objects.all(),
'list_data_tag': Tag.objects.all(),
'list_data_user': User.objects.all(),
'list_data_status': Post.STATUS_ITEMS,
'item_data_tag_list': tag_list
}
return render(request, 'cms/post/publish.html', context=context)
这里要简单说明一下,我们将Post, Category, Tag, User还有Status传送到前端,这个是因为编辑页面需要这些数据;还有一个就是tag list,这个传送到前端是为了在前端展示出来我们已经勾选的tag。
和之前Category与Tag一样,我们在publish.html页面需要修改一下结构,因为我们传入了item_data
数据:
<form class="form-horizontal" action="{% url 'cms:post_add' %}" method="post">
{% if item_data %}
<input type="text" class="form-control" id="id" name="id" value="{{ item_data.id }}" hidden>
{% endif %}
....
<div class="form-group">
<label for="title" class="">Titlelabel>
{% if item_data %}
<input type="text" class="form-control" id="title" name="title" value="{{ item_data.title }}">
{% else %}
<input type="text" class="form-control" id="title" name="title">
{% endif %}
div>
....
<div class="form-group">
<label for="is_md" class="">Is MarkDownlabel>
<select name="is_md" id="is_md" class="custom-select">
{% if item_data %}
可以看到,这里我们还是通过Django的DTL来来做判断,是否传入了item_data
来判断是否显示相对应的数值。
此时我们点击点击一下文章的modify按钮,发现页面如下:
发现和刚才的文章一模一样,接下里就要修改修改过程的后半截了,即处理提交逻辑,这个处理的逻辑主要是在之前的PostView里面,因为我们的button的name不一样,所以通过name来区分是否是修改还是创建,从而处理不同的逻辑:
class PostView(View):
def post(self, request):
# 修改Post
elif 'modify' in request.POST:
form = PostEditForm(request.POST)
if form.is_valid():
id = form.cleaned_data.get('id')
title = form.cleaned_data.get('title')
description = form.cleaned_data.get('description')
author = form.cleaned_data.get('author')
thumbnail = form.cleaned_data.get('thumbnail')
status = form.cleaned_data.get('status')
content = form.cleaned_data.get('content')
is_md = form.cleaned_data.get('is_md')
category = form.cleaned_data.get('category')
priority = form.cleaned_data.get('priority')
is_hot = form.cleaned_data.get('is_hot')
is_top = form.cleaned_data.get('is_top')
is_main_page = form.cleaned_data.get('is_main_page')
publish_time_show = form.cleaned_data.get('publish_time_show')
time_id = form.cleaned_data.get('time_id')
read_num = form.cleaned_data.get('read_num')
tags = form.cleaned_data.get('tag_id')
instance = Post.objects.filter(id=id)
instance.update(title=title, description=description, author=author,
thumbnail=thumbnail, status=status, content=content,
is_md=is_md, category=category, priority=priority,
is_hot=is_hot, is_top=is_top, is_main_page=is_main_page,
publish_time_show=publish_time_show, time_id=time_id,read_num=read_num
)
instance.first().tag.set(tags)
return redirect(reverse("cms:post_manage_view"))
else:
return restful.method_error("Form is error", form.get_errors())
这个时候,我们就去前端测试一下,把各个数据都修改一下,看看能不能成功:
然后点击提交,页面跳回到了管理页面:
发现我们的标题确实改了,我们再去数据库里面核对一下:
看到数据确实是正确的,那么我们的修改就完成了。
Post的删除还是和Category还有Tag的一致,通过JavaScript的Ajax发送POST请求,来删除,具体做法就是写JavaScript代码,视图函数,当然,这里只负责处理逻辑,同样别忘了在urls.py文件里面配置视图函数。
class PostDeleteView(View):
def post(self, request):
post_id = request.POST.get('post_id')
Post.objects.filter(id=post_id).delete()
return restful.ok()
在manag.html页面的按钮,别忘了修改传入post_id
:
<button class="btn btn-danger btn-xs delete-btn" data-post-id="{{ item.id}}">
Delete
button>
写好之后,我们来前端测试一下,在唯一的文章上面点击delete,就会看到页面跳转到了manage.html页面,数据没有哦了。
说明数据删除成功。
最后总结一下,
文章管理开发,基本和Category还有Tag的开发一样:
获取代码的唯一途径:关注『皮爷撸码』,回复『代码』即可获得。
长按下图二维码关注,如文章对你有启发,欢迎在看与转发。