跳到主要内容
Python 实现个人博客系统 | 极客日志
Python 大前端
Python 实现个人博客系统 基于 Flask 框架与 MySQL 数据库构建的个人博客系统。采用 Layui 前端框架与 Markdown 编辑器实现内容发布,包含用户注册登录、密码加密存储、博客 CRUD、评论管理及错误页面处理等核心功能。系统通过 Werkzeug 进行密码安全处理,支持图片上传与模板继承,适合初学者学习 Web 开发全流程。
t ag 发布于 2025/2/6 更新于 2026/5/31 20 浏览前言
项目描述
开发环境:PyCharm、python3.7、MySQL5.5
使用技术:服务端使用 Flask 开发,前端使用 Layui 和 Markdown 编辑器。
项目包含功能如下:
注册:注册账号
登录:通过账号密码进行登录
写博客:使用 Markdown 编辑器完成,可发布博客
我的博客:查看并管理自己发布的博客
我的评论:查看并管理自己的所有评论
修改密码
查看博客列表:查看所有已发布的博客
博客详情页:查看博客内容及评论信息,支持评论
数据库设计
数据库一共设计了三张表:用户表、博客表、评论表。
表之间的映射关系如下:
用户表和博客表一对多关系
用户和评论表一对多关系
博客表和评论表一对多关系
其表的模型类代码如下:
class User (db.Model):
__tablename__ = 'tb_user'
id = db.Column(db.Integer, primary_key=True , autoincrement=True )
username = db.Column(db.String(64 ), unique=True )
password = db.Column(db.String(256 ), nullable=True )
name = db.Column(db.String(64 ))
def password_hash (self, password ):
self .password = generate_password_hash(password)
class Blog (db.Model):
__tablename__ = 'blog'
id = db.Column(db.Integer, primary_key=True , autoincrement=True )
title = db.Column(db.String(128 ))
text = db.Column(db.TEXT)
create_time = db.Column(db.String(64 ))
user_id = db.Column(db.Integer, db.ForeignKey('tb_user.id' ))
user = db.relationship('User' , backref='user' )
(db.Model):
__tablename__ =
= db.Column(db.Integer, primary_key= , autoincrement= )
text = db.Column(db.String( ))
create_time = db.Column(db.String( ))
blog_id = db.Column(db.Integer, db.ForeignKey( ))
user_id = db.Column(db.Integer, db.ForeignKey( ))
blog = db.relationship( , backref= )
user = db.relationship( , backref= )
class
Comment
'comment'
id
True
True
256
64
"blog.id"
"tb_user.id"
"Blog"
"blog"
"User"
"use"
功能实现
页面基本模板实现 页面使用的是 Jinja2 模板,Jinja2 支持页面继承,所以导航栏重复性的页面代码,我们都可以写在一个文件中。这里我们先创建一个 base.html 文件,编写页面大致的框架。其他模块直接继承使用即可。
<!DOCTYPE html >
<html lang ="zh-CN" >
<head >
<meta charset ="UTF-8" >
<meta http-equiv ="X-UA-Compatible" content ="IE=edge" >
<title > {% block title %}{% endblock %}</title >
<link rel ="stylesheet" href ="/static/layui/css/layui.css" >
<link rel ="stylesheet" href ="/static/css/base.css" >
<script src ="/static/js/jquery.js" > </script >
<script src ="/static/layui/layui.js" > </script >
{% block css %}
{% endblock %}
</head >
<body class ="bg" >
<div class ="layui-header" >
<ul class ="layui-nav layui-nav-tree" lay-filter ="" >
<li class ="layui-nav-item" > <a href ="/" > 在线博客平台</a > </li >
{% if username %}
<li class ="layui-nav-item{% block updatepwd_class %}{% endblock %}" > <a href ="/updatePwd" > 修改密码</a > </li >
{% endif %}
<li class ="layui-nav-item{% block blog_class %}{% endblock %}" > <a href ="/blog/blogAll" > 博客</a > </li >
<li class ="layui-nav-item{% block about_class %}{% endblock %}" > <a href ="/about" > 关于</a > </li >
{% if username %}
<li class ="layui-nav-item" style ="float: right; margin-right: 30px;" >
<a href ="javascript:;" > {{ name }}</a >
<dl class ="layui-nav-child" >
<dd > <a href ="/blog/myBlog" > 我的博客</a > </dd >
<dd > <a href ="/blog/myComment" > 我的评论</a > </dd >
<dd > <a href ="/logout" > 注销</a > </dd >
</dl >
</li >
<li class ="layui-nav-item{% block write_class %}{% endblock %}" style ="float: right" > <a href ="/blog/writeBlog" > 写博客</a > </li >
{% else %}
<li class ="layui-nav-item{% block register_class %}{% endblock %}" style ="float: right" > <a href ="/register" > 注册</a > </li >
<li class ="layui-nav-item{% block login_class %}{% endblock %}" style ="float: right" > <a href ="/login" > 登录</a > </li >
{% endif %}
</ul >
</div >
<div class ="layui-container" >
<div class ="layui-content" >
{% block content %}
{# 其他页面内容 #}
{% endblock %}
</div >
</div >
</body >
</html >
这里页面使用了 Layui 定义了一个导航栏,展示了对应的功能模块。其中 {% if username %},username 为后台存放在 session 中的一个键值对,用于判断用户是否登录了,有些功能登录后才显示。
base.html 模板文件完成后,我们在定义一个 index.html 来做项目的首页,直接继承 base.html。这样首页 index.html 就节省了很多代码。
{% extends 'base.html' %}
{% block title %}
在线博客平台
{% endblock %}
{% block content %}
<h1 style ="margin: 35vh;" > 在线博客平台</h1 >
{% endblock %}
登录与注册功能 登录
先定义一个登录的视图函数,可以接收 GET、POST 请求,GET 请求为跳转到登录页面,POST 请求为处理登录提交的请求,验证是否登录成功,登录成功后把当前登录对象的用户名存入 session 会话中。
@index.route('/login' , methods=['POST' , 'GET' ] )
def login ():
if request.method == 'GET' :
return render_template('login.html' )
if request.method == 'POST' :
username = request.form.get('username' )
password = request.form.get('password' )
user = User.query.filter (User.username == username).first()
if (user is not None ) and (check_password_hash(user.password, password)):
session['username' ] = user.username
session.permanent = True
return redirect(url_for('index.hello' ))
else :
flash("账号或密码错误" )
return render_template('login.html' )
登录页面是用 Layui 写的一组 form 表单,也是基础的 base.html。
注册
注册和登录差不多,页面都是使用的同一个 css 样式文件。
修改密码 修改密码模块,因为数据库存放明文密码很不安全,所以这里使用了 Werkzeug 对密码进行了加密存储。对于 Werkzeug 密码加密想进一步了解的,可以参考 Flask 文档。
因为数据库中存储的是加密后的密码,所以这里判断原密码是否正确需要使用 check_password_hash 函数进行判断。
@index.route("/updatePwd" , methods=['POST' , 'GET' ] )
@login_limit
def update ():
if request.method == "GET" :
return render_template("updatePwd.html" )
if request.method == 'POST' :
lodPwd = request.form.get("lodPwd" )
newPwd1 = request.form.get("newPwd1" )
newPwd2 = request.form.get("newPwd2" )
username = session.get("username" )
user = User.query.filter (User.username == username).first()
if check_password_hash(user.password, lodPwd):
if newPwd1 != newPwd2:
flash("两次新密码不一致!" )
return render_template("updatePwd.html" )
else :
user.password_hash(newPwd2)
db.session.commit()
flash("修改成功!" )
return render_template("updatePwd.html" )
else :
flash("原密码错误!" )
return render_template("updatePwd.html" )
写博客 写博客,博客表中会保存标题、博客内容、当前时间等字段。如下是写博客的视图函数。
@blog.route('/writeBlog' , methods=['POST' , 'GET' ] )
@login_limit
def writeblog ():
if request.method == 'GET' :
return render_template('writeBlog.html' )
if request.method == 'POST' :
title = request.form.get("title" )
text = request.form.get("text" )
username = session.get('username' )
create_time = time.strftime("%Y-%m-%d %H:%M:%S" )
user = User.query.filter (User.username == username).first()
blog = Blog(title=title, text=text, create_time=create_time, user_id=user.id )
db.session.add(blog)
db.session.commit()
blog = Blog.query.filter (Blog.create_time == create_time).first()
return render_template('blogSuccess.html' , title=title, id =blog.id )
写博客这里采用的是 Markdown 编辑器。Flask 接收 Markdown 上传图片时的语句:
file = request.files.get('editormd-image-file' )
如果对上述的文件上传代码比较陌生,可以查阅 Flask 文件上传与下载文档。
定义一个文件上传的视图函数(注意 Markdown 上传图片是使用的 POST 方法)。
@blog.route('/imgUpload' , methods=['POST' ] )
@login_limit
def imgUpload ():
try :
file = request.files.get('editormd-image-file' )
fname = secure_filename(file.filename)
ext = fname.rsplit('.' )[-1 ]
fileName = str (uuid.uuid4()) + "." + ext
filePath = os.path.join("static/uploadImg/" , fileName)
file.save(filePath)
return {
'success' : 1 ,
'message' : '上传成功!' ,
'url' : "/" + filePath
}
except Exception:
return {
'success' : 0 ,
'message' : '上传失败'
}
查看博客列表 查看博客列表就是遍历所有已发布的博客。先定义一个视图函数,查询所有已发布的博客,传递到前端进行遍历显示。
@blog.route("/blogAll" )
def blogAll ():
blogList = Blog.query.order_by(Blog.create_time.desc()).all ()
return render_template('blogAll.html' , blogList=blogList)
博客详情页面 在博客列表中点击博客的标题可以进入博客的详情页面,详情页面展示了博客的详细内容以及评论内容。
因为数据库中保存博客内容的是 Markdown 格式的,所以在这里需要解析成 HTML 格式。
editormd.markdownToHTML ("test" , {
htmlDecode : "style,script,iframe" ,
emoji : true ,
taskList : true ,
tex : true ,
flowChart : true ,
sequenceDiagram : true
});
评论
在博客详情页面可以进行评论,评论使用的是 Layui 的编辑器。
评论需要先登录才可以评论,如果没有登录则会提示登录。
@blog.route("/comment" , methods=['POST' ] )
@login_limit
def comment ():
text = request.values.get('text' )
blogId = request.values.get('blogId' )
username = session.get('username' )
create_time = time.strftime("%Y-%m-%d %H:%M:%S" )
user = User.query.filter (User.username == username).first()
comment = Comment(text=text, create_time=create_time, blog_id=blogId, user_id=user.id )
db.session.add(comment)
db.session.commit()
return {
'success' : True ,
'message' : '评论成功!' ,
}
我的博客 登录之后在右上角导航栏可以查看我的博客,查看个人已经发布过的博客并进行管理。
定义一个视图函数,查询当前登录的用户发布的所有博客。
@blog.route("/myBlog" )
@login_limit
def myBlog ():
username = session.get('username' )
user = User.query.filter (User.username == username).first()
blogList = Blog.query.filter (Blog.user_id == user.id ).order_by(Blog.create_time.desc()).all ()
return render_template("myBlog.html" , blogList=blogList)
修改博客 在我的博客中,有修改博客的链接,把当前的博客 id 当做参数传递到后台,查询当前这条博客的数据,进行修改。
@blog.route("/update/<id>" , methods=['POST' , 'GET' ] )
@login_limit
def update (id ):
if request.method == 'GET' :
blog = Blog.query.filter (Blog.id == id ).first()
return render_template('updateBlog.html' , blog=blog)
if request.method == 'POST' :
id = request.form.get("id" )
title = request.form.get("title" )
text = request.form.get("text" )
blog = Blog.query.filter (Blog.id == id ).first()
blog.title = title
blog.text = text
db.session.commit()
return render_template('blogSuccess.html' , title=title, id =id )
删除博客 删除博客和修改一样,把博客的 id 传到后端,根据 id 删除数据库中对应的数据。
@blog.route("/delete/<id>" )
@login_limit
def delete (id ):
blog = Blog.query.filter (Blog.id == id ).first()
db.session.delete(blog)
db.session.commit()
return {
'state' : True ,
'msg' : "删除成功!"
}
我的评论 在页面的右上角不仅可以查看个人已发布的博客,也可以看到自己的所有评论信息。
定义一个视图函数,查询所有的评论内容,返回给前台遍历展示。
@blog.route('/myComment' )
@login_limit
def myComment ():
username = session.get('username' )
user = User.query.filter (User.username == username).first()
commentList = Comment.query.filter (Comment.user_id == user.id ).order_by(Comment.create_time.desc()).all ()
return render_template("myComment.html" , commentList=commentList)
删除评论 在评论列表中有删除评论的链接,根据评论的 id 删除当前条评论,删除后,对应博客中的评论也随之删除。
@blog.route('/deleteCom/<id>' )
def deleteCom (id ):
com = Comment.query.filter (Comment.id == id ).first()
db.session.delete(com)
db.session.commit()
return {
'state' : True ,
'msg' : "删除成功!"
}
关于页面 关于页面可以简单的描述一下网站的设计及作用等,这里就没有写过多的内容了,可以自行设计。
注销 注销只需要清除 session 中的数据,返回首页即可。
@index.route('/logout' )
def logout ():
session.clear()
return redirect(url_for('index.hello' ))
定义错误页面 系统在平时使用中难免会遇到一些错误,但又不能直接让用户看到这些错误,所以我们可以定义一个错误页面,使其报错后都跳转到此页面。
@app.errorhandler(404 )
def page_not_found (e ):
return render_template('404.html' ), 404
@app.errorhandler(500 )
def internal_server_error (e ):
return render_template('500.html' ), 500
安全与优化建议 在实际生产环境中,除了上述基础功能外,还需考虑以下方面以确保系统的安全性和稳定性:
密码安全 :确保使用强哈希算法(如 bcrypt 或 argon2),避免使用简单的 MD5。本示例中使用 Werkzeug 的 generate_password_hash 默认采用 PBKDF2,较为安全。
SQL 注入防护 :Flask-SQLAlchemy 默认使用 ORM,能有效防止 SQL 注入,但仍需避免直接使用字符串拼接 SQL。
XSS 防护 :在渲染用户输入的内容时,确保转义特殊字符。Markdown 编辑器本身提供了一定的防护,但需配置好 htmlDecode 选项。
CSRF 保护 :Flask-WTF 提供了 CSRF 保护机制,建议在表单提交时启用。
性能优化 :对于博客列表查询,当数据量较大时,应添加分页功能;图片资源建议使用 CDN 加速。
部署建议 :生产环境建议使用 Gunicorn 或 uWSGI 作为 WSGI 服务器,配合 Nginx 进行反向代理和静态文件托管。
总结 本文详细介绍了基于 Flask 框架的个人博客系统的实现过程,涵盖了从数据库设计、后端逻辑到前端交互的完整流程。通过该项目,可以深入理解 Web 开发的各个环节,包括路由控制、模板渲染、ORM 操作以及前后端分离的基本思想。
相关免费在线工具 curl 转代码 解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online
Base64 字符串编码/解码 将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
Base64 文件转换器 将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
Markdown转HTML 将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
HTML转Markdown 将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online
JSON 压缩 通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online