恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

Python个人博客系统设计与实现:Flask+MySQL数据库开发全攻略

  • 首页
  • 资讯中心
  • /
  • Python个人博客系统设计与实现:Flask+MySQL数据库开发全攻略

相关资讯

智能体落地实操指南:从论文到可用工具的最小可行路径 2026/9/28 14:22:36
React进阶指南:原理拆解、SSR数据预取与手写迷你实现 2026/9/28 14:22:36
BGA132到BGA154:嵌入式SSD封装规格如何决定性能上限 2026/9/28 14:17:35

最新资讯

Jev凭什么在Agent圈爆火?工具调用与任务拆解实测
离散小波变换MATLAB实战:原理、参数与避坑全解析
2026降AI率实战:检测原理、免费工具与人工改稿技巧
STM32定时器编码器模式测速:从原理到代码实现
2026论文降AI率全攻略:检测原理、工具实测与手改技巧
基于S7-200 PLC的升降横移立体停车库控制系统设计

今日推荐

婚恋网站实战案例:避开3个高价坑,省钱50%还能跑赢流量
制作网页比较方便的软件怎么选?一文搞懂避坑指南
BootCamp6.1.7071驱动包手动安装与回滚全攻略

本周热门

从像素到笔画:srt-whiteboard-animation骨架笔迹追踪实现(Zhang-Suen细化+8邻接追踪)
网站建设的英语怎么说?别只背单词,看完这套安全完整流程才敢上线
新手入门看这篇:建设网站加盟避坑指南与SEO实操

本月精选

自研推理加速器Redwood:两周内实现PyTorch模型高效部署的实战教程
V4L2摄像头采集实战:从camera_client.rar到出图全流程解析
从“谁发明了钢琴键”到知识问答智能体:RAG与记忆工程实践

Python个人博客系统设计与实现:Flask+MySQL数据库开发全攻略

发布时间:2026/9/28 14:22:36
Python个人博客系统设计与实现:Flask+MySQL数据库开发全攻略 简介基于Python的个人博客系统毕业设计完整源码包面向正在学习Python Web开发或准备毕业设计的在校学生帮助他们从零搭建并理解博客类网站的完整实现路径。项目采用Django框架完整实现了文章发布、评论互动、用户注册登录等核心功能并配套数据库脚本myblog.sql从数据库表设计到页面渲染均可对照学习。资源共7387个文件、约25.83MB以py源码文件与html模板为主辅以js、css静态资源、数据库脚本及软件说明书文档目录结构延续Django默认布局便于定位各功能模块目前已有772人学习浏览。项目自带虚拟环境依赖与配置文件解压后可在本地快速部署调试。通过拆解路由配置、ORM数据操作、模板渲染与用户认证模块读者能直接借鉴现成的代码组织方式掌握真实Web项目的分层结构与完整开发思路。1. 一个Python个人博客系统为什么值得你亲手跑一遍打开这份“基于python的个人博客系统的设计与实现包含数据库.zip”里面是一套能直接交付的课程设计源码Python写的Web后端、HTML模板、建表SQL脚本加说明文档。它的价值不在“博客”本身而在把Python入门、数据库增删改查、网页交互和项目答辩串成一条完整链路——对正在做数据库课程设计的人来说这是可以复现的完整作业对想转Python开发的人来说这是理解Flask和MySQL配合方式的最短路径。我见过不少同学拿到压缩包先解压、双击app.py、报错、放弃原因不是代码有问题而是不知道依赖怎么装、数据库怎么建、Python环境怎么配。下面按“选型→建库→写接口→踩坑→验收”的顺序讲清楚每段都给出能直接抄的代码和参数让新手能跟着跑通让熟手能避开最常见的坑。答辩时能现场演示比写满一页纸的架构图有用得多。2. 技术选型与项目骨架先对齐框架、数据库和目录再动手写代码2.1 Flask还是Django课程设计场景下怎么选一个“基于python的个人博客系统”压缩包后端九成是Flask一成是Django。选哪个框架决定你答辩时要讲多少东西也决定你后面改代码的难度。Flask的优势是轻一个app.py就能放下路由、模板渲染和数据库操作代码量一般在500到800行每一步都可以在评审老师面前现场打开讲。Django的优势是齐自带Admin后台、ORM和用户鉴权发文章功能几乎不用自己写但对只学过一个学期Python的同学来说框架内部的中间件、ORM延迟加载机制一旦被追问很容易讲不透。我的建议很直接数据库课程设计或Python实训优先Flask。理由有三个——第一增删改查的SQL是你自己写在代码里的老师问哪一行都能答第二Flask的Session、路由和模板是python教程里出现频率最高的内容出问题容易搜到第三部署简单一个进程就能跑写完能当场演示。如果你拿到的压缩包是Django版本不用急着换。先看目录里有没有manage.py有就是Django只有一个app.py或run.py就是Flask。Django版本的重点是理解ORM的Model类如何映射成表答辩时把“ORM生成SQL”这条讲清楚比背框架文档有用。2.2 数据库选型SQLite起步还是MySQL一步到位标题里“包含数据库”这几个字在不同压缩包里指的是不同东西。有的用SQLite解压就能跑有的用MySQL需要你先建库再启动。这两种我都用过结论是开发调试用SQLite答辩演示用MySQL。SQLite是文件型数据库Python标准库自带驱动零配置适合本地把逻辑跑通但它的并发写很弱写的时候整库加锁数据量超过几千条之后查询也会明显变慢。MySQL需要单独安装服务端但对“数据库课程设计”这类评分场景来说用MySQL更容易得分——评分表里常有“使用专业数据库管理系统”这一项SQLite容易被说成“这不算数据库只是个文件”。对比项SQLiteMySQL安装配置零配置单文件需要安装服务端并启动并发写入弱写时整库锁行级锁支持多用户并发字符集默认UTF-8需要显式指定utf8mb4课程设计观感容易被质疑“太简单”更符合专业数据库要求适合阶段本地开发、单元测试答辩演示、后续上线如果你打算一套代码兼容两种库常见写法是把连接参数集中到config.py里用环境变量切换。下面这份配置我在多个同类项目里都这么写切换时只改DB_TYPE一个值。# config.py import os BASE_DIR os.path.dirname(os.path.abspath(__file__)) DB_TYPE os.getenv(DB_TYPE, sqlite) # sqlite 或 mysql if DB_TYPE mysql: DB_CONFIG { host: os.getenv(DB_HOST, 127.0.0.1), port: int(os.getenv(DB_PORT, 3306)), user: os.getenv(DB_USER, root), password: os.getenv(DB_PASSWORD, root), database: os.getenv(DB_NAME, blog_db), charset: utf8mb4, } else: DB_CONFIG {database: os.path.join(BASE_DIR, blog.db)}说明把数据库参数和环境变量绑在一起是为了答辩现场不用改代码就能换库。演示前执行set DB_TYPEmysqlWindows或export DB_TYPEmysqlLinux/macOS程序读到的就是MySQL配置业务代码一行不动。注意mysql和sqlite分支的字典key不一样db.py里要按分支取用混着写最容易踩空。2.3 解压后第一件事虚拟环境、依赖清单与IDE解释器拿到压缩包别急着双击run.py。先确认python安装没问题再建虚拟环境把依赖装进去。这一步能拦住一半以上的“一运行就报错”。cd blog_system python -m venv venv # Windows 激活 venv\Scripts\activate # Linux / macOS 激活 source venv/bin/activate python --version虚拟环境激活后命令行提示符前面会出现(venv)这时候装的包都只属于这个项目。如果压缩包里有requirements.txt直接pip install -r requirements.txt没有就手动装下面这几个这是这类博客系统最常见的一组依赖。flask pymysql DBUtils cryptography说明几点pymysql是纯Python的MySQL驱动配合DBUtils做连接池cryptography是MySQL 8以上客户端认证必需的没装它pymysql连库时会报cryptography is required这是新手最容易卡住的地方之一。版本号我故意不写死用pip默认装的最新版就行这类课程设计代码对版本兼容性要求不高Python在3.8到3.12之间都能跑。如果你本机是3.13遇到老代码报distutils相关错误直接用conda建一个3.11的虚拟环境最省事别在代码里硬改。环境配好后还要处理IDE的解释器。vscode python环境配置和pycharm配置python环境时都要把解释器指向venv目录下的python。否则会出现一个很常见的现象终端里运行正常IDE里点运行却报ModuleNotFoundError因为IDE默认用了系统Python。这一步本身就是python入门阶段最容易踩的坑换个环境就翻车。注意老代码里如果有db MySQLdb.connect(...)这种写法说明它依赖MySQLdb而不是pymysql需要额外装mysqlclientWindows下编译容易失败建议直接换成pymysql。3. 数据库设计四张表的结构、建表SQL与连接池参数3.1 拆表关系用户、文章、分类、评论怎么关联动手写SQL之前先把数据模型想清楚。一个个人博客系统最少需要四张表user保存用户article保存文章category保存分类comment保存评论。关系是这样一个用户可以发多篇文章一对多一篇文章属于一个分类多对一一篇文章下面有多条评论一对多。做这套系统经常被问一个问题先写数据库还是先写接口我的习惯是先建库。表结构一旦定下来接口参数和页面字段就跟着定了一半后面改SQL的成本远低于改接口逻辑。反过来先写接口经常出现“接口要返回作者名但article表里只有user_id”这种返工。这里有个设计细节值得注意评论表要不要存评论者的用户名我的做法是不存只存user_id展示时通过JOIN关联user表取username。理由有两个——第一避免用户名修改后历史评论显示旧名字第二这是数据库范式里“消除传递依赖”的直观例子答辩时能主动讲出来是加分项。分类表同理article表里存category_id而不是分类名字符串。另一个常见的数据库知识点概念是外键和级联删除。设计上删除用户时他的文章和评论应该一并处理删除文章时它的评论应该一并删除。建表时用外键加ON DELETE CASCADE就能自动完成不用在Python代码里手动删。不少课程设计代码图省事不建外键结果演示“删除用户”时残留一堆孤儿数据反而被扣分。3.2 建表SQL字段类型、字符集与索引怎么定下面这份SQL是这类博客系统最稳的建表脚本安装MySQL并启动服务后执行mysql -uroot -p blog.sql一次导入即可。CREATE DATABASE IF NOT EXISTS blog_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE blog_db; CREATE TABLE user ( id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, username VARCHAR(50) NOT NULL UNIQUE, password_hash VARCHAR(255) NOT NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE category ( id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, name VARCHAR(50) NOT NULL UNIQUE ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE article ( id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, title VARCHAR(200) NOT NULL, content TEXT NOT NULL, user_id INT UNSIGNED NOT NULL, category_id INT UNSIGNED NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, INDEX idx_user(user_id), INDEX idx_category(category_id), INDEX idx_created(created_at), CONSTRAINT fk_article_user FOREIGN KEY (user_id) REFERENCES user(id) ON DELETE CASCADE ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE comment ( id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, article_id INT UNSIGNED NOT NULL, user_id INT UNSIGNED NOT NULL, content VARCHAR(1000) NOT NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, INDEX idx_article(article_id), CONSTRAINT fk_comment_article FOREIGN KEY (article_id) REFERENCES article(id) ON DELETE CASCADE, CONSTRAINT fk_comment_user FOREIGN KEY (user_id) REFERENCES user(id) ON DELETE CASCADE ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;参数说明id统一用INT UNSIGNED AUTO_INCREMENT个人博客的数据量到不了BIGINT的量级UNSIGNED把上限翻倍到42亿够用且省空间。username用VARCHAR(50)并加UNIQUE保证注册不重名密码不存明文存password_hash长度给到255兼容werkzeug生成的哈希串。content用TEXT而不是VARCHAR因为VARCHAR在MySQL里的上限是65535字节一篇长文章很容易超。索引选择article表在user_id、category_id、created_at各建一个普通索引。外键列建索引是InnoDB的硬性要求不建外键也建议建否则按作者查文章的JOIN会全表扫描created_at索引是给“首页按时间倒序取10篇”这条高频查询用的。engine统一InnoDB支持事务和外键MyISAM虽然查询快一点但不支持外键约束答辩被问“什么是事务”时你没法现场演示。字符集统一utf8mb4而不是utf8因为utf8在MySQL里是utf8mb3存不了emoji和生僻字评论区一个表情就把写入搞崩。提示如果你的压缩包里只有SQLite的blog.db文件而没有SQL脚本用sqlite3命令执行.schema article查看表结构对照。SQLite不强制类型但也别因此就不设字段规范后面Python代码里的长度校验才是真正防线。3.3 Python连库裸连接、连接池与并发控制连接数据库的代码新手容易写成“每次请求都新建连接、用完就丢”。本地演示看不出问题一旦多个用户同时访问MySQL会迅速打满连接数报Too many connections。这类博客系统更稳的做法是用DBUtils维护一个MySQL连接池。# db.py import pymysql from dbutils.pooled_db import PooledDB import config if config.DB_TYPE mysql: pool PooledDB( creatorpymysql, maxconnections10, # 池里最多10个连接 mincached2, # 启动就预建2个空闲连接 maxcached5, # 空闲连接最多保留5个 blockingTrue, # 连接被占满时排队等待而不是直接报错 ping1, # 每次取连接前ping一次防止拿到死连接 **config.DB_CONFIG ) def db_execute(sql, argsNone, fetchFalse): conn pool.connection() try: with conn.cursor() as cur: cur.execute(sql, args) if fetch: return cur.fetchall() conn.commit() return cur.lastrowid finally: conn.close() # 归还连接不是真正关闭 else: import sqlite3 def db_execute(sql, argsNone, fetchFalse): conn sqlite3.connect(config.DB_CONFIG[database]) try: cur conn.execute(sql, args or ()) if fetch: return cur.fetchall() conn.commit() return cur.lastrowid finally: conn.close()参数说明maxconnections设10个人博客并发量撑死几十10个连接足够设太大反而浪费MySQL内存。blockingTrue是关键连接被占满时请求会排队而不是抛异常演示时快速刷页面不会突然报错。ping1表示取连接前执行一次探活MySQL的wait_timeout默认8小时空闲连接会被服务端断开不ping就拿到废连接。conn.close()在连接池模式里不是真关闭而是把连接归还池子这点答辩时主动讲出来很加分。关注数据库并发锁的话要注意InnoDB的行级锁只在事务里生效上面代码每条SQL是独立自动提交的单字段写入没有并发问题。但如果后面做“文章阅读数1”这类操作一定要写成UPDATE article SET viewsviews1 WHERE id%s而不是先在Python里查出来再加否则两个请求同时读到100写回去都是101这就是典型的并发覆盖也是“先查后改”这个坏习惯的代价。4. 核心功能实现注册登录、文章增删改查与评论的完整链路4.1 注册登录与密码哈希不存明文用Session维持状态先用两段代码把用户这块落地。注册时密码用werkzeug的generate_password_hash做哈希再入库登录时用check_password_hash比对。为什么不存明文或MD5这是答辩必问题明文泄露等于所有密码泄露MD5用彩虹表几十秒就能反查werkzeug默认的pbkdf2:sha256加了随机盐同一密码每次哈希结果都不同。# auth.py from flask import Flask, request, redirect, url_for, session from werkzeug.security import generate_password_hash, check_password_hash from db import db_execute app Flask(__name__) app.secret_key course-design-demo-key # 上线前换成随机字符串 app.route(/register, methods[POST]) def register(): username request.form.get(username, ).strip() password request.form.get(password, ) if not (4 len(username) 20 and len(password) 6): return 用户名4-20位密码至少6位, 400 exists db_execute( SELECT id FROM user WHERE username%s, (username,), fetchTrue ) if exists: return 用户名已存在, 400 db_execute( INSERT INTO user(username, password_hash) VALUES(%s, %s), (username, generate_password_hash(password)), ) return redirect(url_for(login)) app.route(/login, methods[POST]) def login(): username request.form.get(username, ).strip() password request.form.get(password, ) row db_execute( SELECT id, username, password_hash FROM user WHERE username%s, (username,), fetchTrue ) if row and check_password_hash(row[0][2], password): session.clear() # 登录成功前清一次防会话固定 session[user_id] row[0][0] session[username] row[0][1] return redirect(url_for(index)) return 用户名或密码错误, 400逻辑说明SQL都用%s占位符传参参数由pymysql转义这是防SQL注入的第一道防线。session.clear()是容易漏的细节——不清理就设置登录态攻击者可以提前把自己的session塞进你浏览器。Flask的session是签名Cookie默认存在客户端但内容不可篡改验证逻辑依赖secret_key所以这个key绝不能顶着默认值上线。4.2 文章发布与修改把增删改查写成一组路由文章的增删改查就是“数据库增删改查”这道大题的完整答案。下面代码覆盖新增、列表、详情、更新和删除五条路径更新和新增共用同一个表单模板。# routes_article.py from flask import request, redirect, url_for, render_template, session, abort from db import db_execute app.route(/post/new, methods[GET, POST]) def post_new(): if user_id not in session: return redirect(url_for(login)) if request.method POST: title request.form.get(title, ).strip() content request.form.get(content, ).strip() category_id request.form.get(category_id, typeint) if not title or not content: return 标题和内容不能为空, 400 article_id db_execute( INSERT INTO article(title, content, user_id, category_id) VALUES(%s, %s, %s, %s), (title, content, session[user_id], category_id), ) return redirect(url_for(article_detail, article_idarticle_id)) categories db_execute(SELECT id, name FROM category, fetchTrue) return render_template(post_form.html, categoriescategories) app.route(/post/int:article_id/edit, methods[POST]) def post_edit(article_id): if user_id not in session: return redirect(url_for(login)) row db_execute( SELECT id, user_id FROM article WHERE id%s, (article_id,), fetchTrue ) if not row: abort(404) if row[0][1] ! session[user_id]: return 无权修改别人的文章, 403 title request.form.get(title, ).strip() content request.form.get(content, ).strip() db_execute( UPDATE article SET title%s, content%s, updated_atNOW() WHERE id%s, (title, content, article_id), ) return redirect(url_for(article_detail, article_idarticle_id)) app.route(/post/int:article_id/delete, methods[POST]) def post_delete(article_id): if user_id not in session: return redirect(url_for(login)) row db_execute( SELECT user_id FROM article WHERE id%s, (article_id,), fetchTrue ) if not row: abort(404) if row[0][0] ! session[user_id]: return 无权删除, 403 db_execute(DELETE FROM article WHERE id%s, (article_id,)) return redirect(url_for(index))逻辑说明增删改三条路径都要求先登录操作前先查article的user_id再和session里的user_id比对防止越权改删别人的文章。“先校验、后更新”的顺序很重要写成先更新再判断就晚了。DELETE语句依赖外键的ON DELETE CASCADE文章删掉后comment自动清空不需要额外写删除评论的代码。updated_atNOW()配合建表时的ON UPDATE CURRENT_TIMESTAMP双保险保证时间戳一定会刷新。4.3 评论功能与分页JOIN查询和LIMIT/OFFSET的参数选择评论写入的核心是两件事限制内容长度、防止未登录刷评论。分页是列表页的重点下面的写法考虑了参数类型安全和总页数计算。app.route(/) def index(): page request.args.get(page, 1, typeint) # 非数字自动回退1 page max(page, 1) page_size 10 offset (page - 1) * page_size total db_execute( SELECT COUNT(*) FROM article, fetchTrue )[0][0] rows db_execute( SELECT a.id, a.title, a.created_at, u.username, (SELECT COUNT(*) FROM comment c WHERE c.article_ida.id) cnt FROM article a JOIN user u ON a.user_idu.id ORDER BY a.created_at DESC LIMIT %s OFFSET %s, (page_size, offset), fetchTrue ) pages (total page_size - 1) // page_size return render_template(index.html, rowsrows, pagepage, pagespages) app.route(/post/int:article_id, methods[GET, POST]) def article_detail(article_id): if request.method POST: if user_id not in session: return redirect(url_for(login)) content request.form.get(content, ).strip() if not content or len(content) 1000: return 评论内容不能为空且不超过1000字, 400 db_execute( INSERT INTO comment(article_id, user_id, content) VALUES(%s, %s, %s), (article_id, session[user_id], content), ) return redirect(url_for(article_detail, article_idarticle_id)) row db_execute( SELECT a.*, u.username FROM article a JOIN user u ON a.user_idu.id WHERE a.id%s, (article_id,), fetchTrue ) comments db_execute( SELECT c.content, c.created_at, u.username FROM comment c JOIN user u ON c.user_idu.id WHERE c.article_id%s ORDER BY c.created_at ASC, (article_id,), fetchTrue ) if not row: abort(404) return render_template(detail.html, articlerow[0], commentscomments)逻辑说明page从URL取加了typeint用户传pageabc时Flask自动返回默认值1不会让代码崩在类型转换上。评论计数用了相关子查询数据量小没关系如果文章上万篇这个子查询会成为性能瓶颈届时就该在article表冗余一个comment_count字段但课程设计阶段不用提前优化。LIMIT/OFFSET分页在数据量超过十万条后会有深度翻页变慢的问题个人博客远到不了这个量级理解这一层边界就够了答辩时能说出“深分页的代价”是加分项。评论排序用ASC正序符合“楼上的评论在上面”的阅读习惯文章列表用倒序最新的在最上面两种方向各有用处别顺手都写成DESC。5. 避坑记录博客系统从“能跑”到“能演示”的5个翻车点5.1 启动报ModuleNotFoundError: No module named flask现象双击run.py或IDE里运行第一行报错ModuleNotFoundError提示找不到flask或pymysql。原因装完依赖后没重新激活虚拟环境或者IDE解释器还指在系统Python上。如果压缩包里自带了一个venv文件夹那个venv里写的是作者本机的绝对路径复制到别的电脑后整个失效报错也是ModuleNotFoundError。解决先用pip list看flask在不在当前环境在的话检查IDE右下角解释器路径是不是venv下的python.exe。最彻底的排查办法是在终端手动激活venv再运行终端能跑而IDE不能就是解释器选错的问题vscode里用CtrlShiftP调出Python: Select Interpreter重新指定。如果带的是旧venv把项目里的venv文件夹整个删掉重新python -m venv venv这步谁也绕不过去。5.2 中文全部变成问号现象数据库里中文文章标题和内容能写入但页面显示全是问号或者MySQL命令行查询出来是乱码。原因建库时没指定字符集。MySQL 8默认是utf8mb4但如果你用的是MySQL 5.7默认latin1中文一写入就丢字符。另一层是连接层字符集pymysql参数里charset没写默认按latin1传输照样乱码。解决分层查。第一层建库SQL里显式写DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci我前面的脚本已经带了第二层pymysql连接参数里的charsetutf8mb4也要写两层不一致等于白设第三层HTML模板的meta标签如果写的是charsetgb2312Flask返回UTF-8内容也会乱统一改成UTF-8。三层按顺序查一遍中文乱码基本都能救回来。5.3 连接池报TimeoutError或Lost connection现象本地开几个浏览器标签同时访问过一会儿报pymysql.err.OperationalError提示Lost connection或TimeoutError重启程序又好了过一阵又犯。原因maxconnections太小连接全被占用blockingFalse时直接抛异常或者连接长时间空闲被MySQL的wait_timeout断掉。开发机不关机挂一晚上第二天第一次请求大概率拿到死连接。解决把maxconnections调到10到20blocking设为True连接池参数加ping1每次取连接时探活。MySQL的wait_timeout默认28800秒也就是8小时这正是ping要解决的问题。真嫌参数记不住就在连接成功后的第一条SQL执行SET SESSION wait_timeout28800但连接池方案里没必要ping1已经覆盖了。5.4 登录接口被一个引号打穿现象注册一个用户名为 OR 11的账号或者登录时输入特殊字符页面返回了不该有的数据甚至报SQL语法错误。原因SQL用字符串拼接而不是参数化。SELECT * FROM user WHERE username%s % username这种写法输入引号就改变了SQL结构这就是教科书级的SQL注入。在个人博客系统里它偷不到多少数据但答辩演示时被老师在登录框里敲一个引号当场翻车比任何扣分项都致命。解决全部改成占位符传参。pymysql里用%s占位执行时传入args元组由驱动负责转义SQLite分支用?占位。这是最基本的安全红线课程设计评分表里如果列了“防止SQL注入”这题是送分的——把两段代码对比着写进报告就行。5.5 刷新页面就掉登录或者一关浏览器就得重登现象登录后能正常操作但点击文章链接跳到新页面就要求重新登录或者关闭浏览器再打开登录态完全丢失。原因Flask的session默认是临时的浏览器关闭即失效跳转掉登录通常是secret_key没设置或者每次启动时随机生成导致签名Cookie在程序重启后校验失败——你前两天还能用今天一改代码重新run登录态全没了这就是典型的黑匣子问题。解决app.secret_key设成固定字符串放到config.py里统一管理如果需要“记住我”功能登录成功后加session.permanent True并设置会话生命周期。from datetime import timedelta app.permanent_session_lifetime timedelta(days7) # 登录成功后 session.permanent True注意secret_key固定后一旦泄露要第一时间更换否则任何人都能伪造登录态。另外生产环境别用Flask自带的开发服务器跑接上gunicorn之前先确认secret_key没有散落在代码里。这五条是我在不同机器上帮人排过最多的问题前两条属于环境后三条属于代码习惯。评审现场最怕的不是功能缺失而是演示时当场报错——上面五条按顺序自查一遍演示翻车的概率能降一大半。6. 验收与提交一条龙测试清单和三个加分细节6.1 全链路验收从注册到评论一次跑完用浏览器手点也能验收但更稳的是写一个请求脚本把注册、登录、发文章、评论、改文章、删文章六步串起来跑一遍就知道哪一环断了。# 验收脚本 demo_check.py import requests base http://127.0.0.1:5000 s requests.Session() s.post(base /register, data{username: test01, password: 123456}) s.post(base /login, data{username: test01, password: 123456}) r s.post(base /post/new, data{title: 验收文章, content: 正文内容, category_id: 1}) aid r.headers.get(Location, ).split(/)[-1] s.post(base f/post/{aid}, data{content: 第一条评论}) print(注册/登录/发文章/评论 全部通过文章ID:, aid)跑通后把下面这张清单打印出来贴到报告附录里评审老师会认为你做完了完整的系统测试注册重复用户名被拦截、未登录访问写作页被重定向、越权改别人文章返回403、空标题被拒绝、评论超1000字被拒绝、删除文章后关联评论自动消失。6.2 答辩前三个加分细节第一把代码里的print全部换成logging按时间戳输出到run.log演示时当场打开日志文件比嘴上说“我调试过了”有说服力得多。第二写一个备份脚本把blog.sql用mysqldump定期导出压缩包提交时带上一份干净的原始数据库脚本老师导入就能复现这是最容易拿的印象分。第三作者权限之外的“阅读数统计”可以不做但要在报告里写明“为什么不做”能体现边界意识。我自己做这类课程设计时养成的习惯是把演示流程至少完整跑三遍——第一遍按正常路径第二遍故意输入错误数据第三遍拔掉数据库服务看报错是否友好。三遍下来心里就有底了。希望帮到你。本文还有配套的精品资源点击获取

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号