第四篇:BBS后台管理、编辑器和修改头像
第四篇:BBS后台管理、编辑器和修改头像
一、后台管理
1、页面搭建
"""
当一个文件夹下文件比较多的时候,可以继续创建文件夹分类处理
templates文件夹
backend文件夹
应用1文件夹
应用2文件夹
"""
我们简单建立一个后台界面,效果如下所示。
将个人站点的文章全部显示出来,并可以进行相应的操作。
我们使用下面的路由进行配置。
"""urls.py"""
# 后台管理
url(r'^backend/', views.backend),
"""views.py"""
# 后台管理
def backend(request):
# 拿到该用户的文章数据
article_queryset = models.Article.objects.filter(blog=request.user.blog)
page_obj = Pagination(request.GET.get('page', 1), article_queryset.count())
article_queryset = article_queryset[page_obj.start: page_obj.end]
return render(request, 'backend/backend.html', locals())
"""backend/backend.html"""
{% extends 'backend/backend_base.html' %}
{% block article %}
<table class="table table-striped table-hover">
<thead>
<tr>
<th>标题</th>
<th>点赞数</th>
<th>评论数</th>
<th>操作</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{% for article_obj in article_queryset %}
<tr>
<td><a href="/{{ request.user.username }}/article/{{ article_obj.pk }}/">{{ article_obj.title }}</a></td>
<td>{{ article_obj.up_num }}</td>
<td>{{ article_obj.comment_num }}</td>
<td><a href="">编辑</a></td>
<td><a href="">删除</a></td>
</tr>
{% endfor %}
</tbody>
</table>
<!--分页器展示-->
<div class="pull-right">
{{ page_obj.page_html| safe }}
</div>
{% endblock %}
{% block js %}
{% endblock %}
我们将后台管理所有的代码都放到backend
文件夹下,文件夹效果如下所示。
2、添加文章
我们点击图片中的添加文章。
可以跳转到如下界面。
这里我们使用了富文本编辑器kindeditor
,使用方法参考kindeditor文档。
我们将kindeditor导入到进行文件夹static
下,在添加文章界面add_article.html
中导入即可。
# 导入方式
<script charset="utf-8" src="/static/kindeditor/kindeditor-all-min.js"></script>
# 参数的调整,我们可以参考官方提供的文档,对显示效果进行一个调整。具体方式参考文档。
<script>
KindEditor.ready(function(K) {
window.editor = K.create('#editor_id',{
width: '100%', // 设置宽度
height: '400px', // 高度
resizeType: 1, // 宽不变,高便
uploadJson : '/upload_image/', // 上传图片的后端提交路径
extraFileUploadParams : {
'csrfmiddlewaretoken':'{{ csrf_token }}' // 提交额外参数
}
});
});
</script>
另外,添加文章的时候,还有两个需要注意的问题。
# 1.文章的简介
不能直接切去,应该先想办法获取到当前页面的文本内容之后截取150个文本字符。
# 2.XSS攻击
针对支持用户直接编写html代码的网址,针对用户直接书写的script标签,我们需要处理。
"""针对xss攻击"""
方式一:注释标签内部的内容
方法二:直接将script删除
"""如何解决?"""
如果是我们自己解决,
针对1:针对文章的简介,后端直接通过正则表达式筛选。
针对2:首先需要确定及获取script标签
这两步似乎都很麻烦,我们使用下面的模块 beautifulsoup4
安装: pip3 install beautifulsoup4
beautifulsoup4的具体使用方法如下。
# 模块使用
soup = BeautifulSoup(content, 'html.parser')
tags = soup.find_all()
# 获取所有的标签
for tag in tags:
# print(tag.name) # 获取页面所有的标签
# 针对script标签 直接删除
if tag.name == 'script':
# 删除标签
tag.decompose()
"""
# 文章简介
# 1 先简单暴力的直接切去content 150个字符
# desc = content[0:150]
"""
# 2 截取文本150个
desc = soup.text[0:150]
3、编辑器上传图片
我们发现,直接使用别人提供的编辑器,别人已经写好了接口,但是不是自己写的,对于一些参数,需要我们自己手动进行修改。
直接用编辑器,图片是不能够上传的,配置如下。
"""add_article.html"""
<script>
KindEditor.ready(function(K) {
window.editor = K.create('#editor_id',{
width: '100%',
height: '400px',
resizeType: 1,
uploadJson : '/upload_image/', // 上传图片的后端提交路径
extraFileUploadParams : {
'csrfmiddlewaretoken':'{{ csrf_token }}'
}
});
});
</script>
"""views.py 中 upload_image方法"""
import os
from BBS import settings
# 编辑器上传图片接口
def upload_image(request):
"""
//成功时
{
"error" : 0,
"url" : "http://www.example.com/path/to/file.ext"
}
//失败时
{
"error" : 1,
"message" : "错误信息"
}
"""
back_dic = {'error': 0, } # 先提前定义返回给编辑器的数据格式
"""用户写文章上传的图片,也算静态资源,也应该放到media文件夹下"""
if request.method == "POST":
# 获取用户上传的图片对象
# print(request.FILES) # 打印看到了键名固定叫imgFile
file_obj = request.FILES.get('imgFile')
# 手动拼接存储文件的路径
file_dir = os.path.join(settings.BASE_DIR, 'media', 'article_img')
# 优化操作 先判断当前文件夹是否存在 不存在 自动创建
if not os.path.isdir(file_dir):
os.mkdir(file_dir) # 创建一层目录结构 article_img
# 拼接图片的完整路径
file_path = os.path.join(file_dir, file_obj.name)
with open(file_path, 'wb') as f:
for line in file_obj:
f.write(line)
back_dic['url'] = '/media/article_img/{}'.format(file_obj.name)
return JsonResponse(back_dic)
配置好了,我们随便使用一张图片进行测试。发现可以实现编辑器上传图片功能。
到这里之后,我们便可以实现一个文章的添加,效果展示如下。
我们查看文章,发现文章可以正常渲染。
4、添加文章完整代码
- add_article.html
{% extends 'backend/backend_base.html' %}
{% block article %}
<h3>添加标题</h3>
<form action="" method="post" enctype="multipart/form-data">
{% csrf_token %}
<p>
标题:<input type="text" name="title" class="form-control">
</p>
<p>文本框:</p>
<textarea name="content" id="editor_id" cols="60" rows="10"></textarea>
<p>分类</p>
<p>
{% for category_obj in category_queryset %}
<input type="radio" name="category" value="{{ category_obj.pk }}">{{ category_obj.name }}
{% endfor %}
</p>
<p>标签</p>
<p>
{% for tag_obj in tag_queryset %}
<input type="checkbox" name="tag" value="{{ tag_obj.pk }}">{{ tag_obj.tag }}
{% endfor %}
</p>
<input type="submit" class="btn btn-success" value="发布文章">
</form>
{% endblock %}
{% block js %}
<script charset="utf-8" src="/static/kindeditor/kindeditor-all-min.js"></script>
<script>
KindEditor.ready(function(K) {
window.editor = K.create('#editor_id',{
width: '100%',
height: '400px',
resizeType: 1,
uploadJson : '/upload_image/', // 上传图片的后端提交路径
extraFileUploadParams : {
'csrfmiddlewaretoken':'{{ csrf_token }}'
}
});
});
</script>
{% endblock %}
- views.py 中 add_article 函数
from bs4 import BeautifulSoup
# 后台管理_添加文章
@login_required(login_url='/login/')
def add_article(request):
if request.method == 'POST':
# 拿到post过来的数据
title = request.POST.get('title')
content = request.POST.get('content')
category_id = request.POST.get("category")
tag_id_list = request.POST.getlist('tag')
# 模块使用
soup = BeautifulSoup(content, 'html.parser')
tags = soup.find_all()
# 获取所有的标签
for tag in tags:
# print(tag.name) # 获取页面所有的标签
# 针对script标签 直接删除
if tag.name == 'script':
# 删除标签
tag.decompose()
"""
# 文章简介
# 1 先简单暴力的直接切去content 150个字符
# desc = content[0:150]
"""
# 2 截取文本150个
desc = soup.text[0:150]
article_obj = models.Article.objects.create(
title=title,
content=str(soup),
desc=desc,
category_id=category_id,
blog=request.user.blog
)
# 文章和标签的关系表 是我们自己创建的 没法使用add set remove clear方法
# 自己去操作关系表 一次性可能需要创建多条数据 批量插入bulk_create()
article_obj_list = []
for i in tag_id_list:
tag_article_obj = models.Article2Tag(article=article_obj, tag_id=i)
article_obj_list.append(tag_article_obj)
# 批量插入数据
models.Article2Tag.objects.bulk_create(article_obj_list)
# 跳转到后台管理文章展示页
return redirect('/backend/')
# 属于个人内容的分类内容
category_queryset = models.Category.objects.filter(blog=request.user.blog)
# 属于个人用户的标签内容
tag_queryset = models.Tag.objects.filter(blog=request.user.blog)
return render(request, 'backend/add_article.html', locals())
-
views.py 中 配置图片上传
见上所示
二、修改头像
只需要注意一下avatar对象的更新方式即可。
- set_avatar.html
{% extends 'base.html' %}
{% block content %}
<h3 class="text-center">修改头像</h3>
<form action="" method="post" enctype="multipart/form-data">
{% csrf_token %}
<!--原头像-->
<p>
<label for="">原头像:</label>
<img src="/media/{{ request.user.avatar }}" alt="" width="150px">
</p>
<!--新头像-->
<p>
<label for="myfile">新头像:
{% load static %}
<img src="{% static 'img/default.png' %}" id='myimg' alt="" width="150px" style="margin-left: 10px">
</label>
<input type="file" id="myfile" name="avatar" style="display: none" >
</p>
<input type="submit" class="btn btn-info">
</form>
{% endblock %}
{% block js %}
<script>
$("#myfile").change(function () {
// 文件阅读器对象
// 1 先生成一个文件阅读器对象
let myFileReaderObj = new FileReader();
// 2 获取用户上传的头像文件
let fileObj = $(this)[0].files[0];
// 3 将文件对象交给阅读器对象读取
myFileReaderObj.readAsDataURL(fileObj) // 异步操作 IO操作
// 4 利用文件阅读器将文件展示到前端页面 修改src属性
// 等待文件阅读器加载完毕之后再执行
myFileReaderObj.onload = function(){
$('#myimg').attr('src',myFileReaderObj.result)
}
})
</script>
{% endblock %}
- views.py
# 修改头像
@login_required(login_url='/login/')
def set_avatar(request):
if request.method == 'POST':
file_obj = request.FILES.get('avatar')
# models.UserInfo.objects.filter(pk=request.user.pk).update(avatar=file_obj) # 不会再自动加avatar前缀
# 1.自己手动加前缀
# 2.换一种更新方式
user_obj = request.user
user_obj.avatar = file_obj
user_obj.save()
return redirect('/home/')
blog = request.user.blog
username = request.user.username
return render(request, 'set_avatar.html', locals())
三、BBS总结
到了这里,我们发现已经基本可以实现一个博客园的效果,当然,是lowb版本,这里进行简单总结。
"""注册功能"""
forms组件使用
头像动态展示
错误信息提示
"""登陆功能"""
图片验证码
滑动验证码
"""首页展示"""
media配置
主动暴露任意资源接口
"""个人站点展示"""
侧边栏展示
侧边栏筛选
侧边栏inclusion_tag
"""文章详情页"""
点赞点踩
评论
"""后台管理"""
"""修改头像"""
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人