第四篇: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

"""文章详情页"""
点赞点踩
评论

"""后台管理"""
 
"""修改头像"""
posted @   YangYi215  阅读(385)  评论(0编辑  收藏  举报
编辑推荐:
· 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训练数据并当服务器共享给他人
点击右上角即可分享
微信分享提示