简介:基于Flask框架的二手交易购物商城毕业设计源码包,主要面向Python方向的毕业设计、课程设计以及希望提升Web开发技能的初学者。项目完整覆盖前台商品展示、购物流程、后台管理、用户认证与交易记录等核心业务,能够帮助读者快速理解Flask从请求处理到模板渲染的完整工作链路。压缩包内含201个文件,核心包括32个Python源码文件、9个HTML页面模板、25个JavaScript与24个CSS样式资源,以及多个图片、数据库脚本和项目配置文件,总大小约14.05MB,目录结构规范,便于按模块查阅与二次开发。目前已有222人学习浏览,适合作为高校项目实战和答辩参考。通过学习该项目,可以深入掌握Flask应用搭建、使用SQLAlchemy操作MySQL数据库、借助Redis优化会话与热点数据读取,并理解静态资源管理、模板继承、配置分离等开发习惯,同时还能看到登录注册、商品发布、订单处理等功能的实现思路,是一份兼顾学习价值与项目完整度的毕业设计案例。 很多做毕业设计的同学看到“基于Flask框架二手交易购物商城”这种题目,第一反应是先找源码,然后折腾环境,最后卡在Redis和MySQL的安装配置上大半天,心态直接崩掉。这篇文章不打算讲那种“点击下一步”的菜鸟教程,而是把整套项目从技术选型、环境搭建到核心模块实现的完整链路拆开揉碎,说说每一环为什么这么做、实际落地的时候会踩哪些坑。内容基于我前后帮人调过不下十个同类项目的经验,适合正在做毕设、或者想拿Flask做点完整东西练手的同学作为参考。
1. 项目整体设计与技术选型思路
1.1 为什么毕设选Flask而不是Django或SpringBoot
二手交易商城听起来是个电商系统,很多同学第一反应是用Django,自带Admin后台、ORM、认证体系,配套齐全。但放在毕业设计的场景里,Flask反而是更合理的选择。原因很简单:Django的太“全”了,很多功能框架帮你做完了,答辩的时候老师问“这个用户认证是怎么实现的”,你只能说“Django自带的”,说不出底层原理。Flask不一样,它只提供最核心的请求分发和模板渲染,其他所有东西都要自己动手拼装,正因如此,你才有机会把每个环节吃透,在答辩的时候自信地拆解。
Flask的另一个优势是轻量、容易理解。一个二手交易商城,核心功能无非是登录注册、商品发布、商品浏览搜索、下单交易、个人中心这几块,用Flask加蓝图(Blueprint)划分模块,逻辑清晰,代码量适中。我见过有人用SpringBoot写同样的项目,光环境配置就够写好几篇文档,对只学过Python基础的学生来说门槛太高。Flask这套技术栈,从安装到跑起来,半小时以内搞定,省下来的时间可以专心打磨业务逻辑。
1.2 Flask、Redis、MySQL各自扮演什么角色
这个项目里,三个核心组件分工明确,谁也不会抢谁的活儿。
MySQL是整个系统的最底层数据仓库,用户信息、商品信息、订单记录、交易评价这些结构化数据都落在MySQL里。选择MySQL而不是SQLite,一方面是因为SQLite对并发写入支持弱,商城这类场景下容易出现“database is locked”错误;另一方面是MySQL是企业中最常用的数据库,写到简历里更有说服力。
Redis则承担两件事:缓存和会话保持。商品详情、首页推荐列表这类读多写少的接口,把查询结果缓存进Redis,能显著降低MySQL的压力。Flask的session默认使用客户端签名Cookie存储,但Cookie有大小限制且容易被篡改,引入Redis后可以把session数据存到服务端,只在客户端种一个session_id,安全性高不少。对于二手商城这种需求,Redis还特别适合做商品浏览历史记录、购物车临时数据、验证码存储这些场景,都是天然有有效期概念的数据。
PyCharm在这个项目里是开发调试的IDE。说实话用什么编辑器不影响代码本身的运行,但PyCharm的调试器、SSH远程解释器、数据库工具面板,能省掉很多排查问题的时间。到项目后期,debug断点看请求上下文、用内置的Database面板直接查表结构,这些便利性是真的能提升效率的。
1.3 功能模块划分:一个标准的商城项目长什么样
从功能上讲,二手交易商城和普通电商的核心差异在于“闲置物品转卖”这个定位,没有复杂的SKU规格、没有库存管理、没有多级分销。基于Flask框架的经典模块划分大概是这样的:
- 用户模块:注册、登录、注销、个人资料编辑、密码修改
- 商品模块:商品发布、商品列表、商品详情、商品下架、分类筛选、关键词搜索
- 交易模块:购物车、生成订单、订单列表、订单详情、确认收货、评价
- 管理后台:用户管理、商品审核、订单管理、数据统计
如果用蓝图来组织代码,目录结构应该是:
app/ __init__.py # Flask应用工厂 models.py # 所有模型定义(User, Item, Order等) blueprints/ auth.py # 登录注册相关路由 item.py # 商品相关路由 order.py # 订单相关路由 admin.py # 管理后台路由 utils.py # 自定义装饰器、工具函数 config.py # 配置文件(MySQL、Redis连接参数) run.py # 程序入口这个结构的好处是每个蓝图负责一组相关的URL规则,互不干扰,后期出问题也好定位。我见过很多同学的代码,把所有路由都堆在同一个文件里,几百行代码一个文件搞定,跑是能跑,但到了答辩演示或者要加功能的时候,改起来极其痛苦。
2. 环境搭建与工具配置的实操细节
2.1 Python虚拟环境:基本功不能省
开搞之前先创建虚拟环境,不要用全局Python直接装包。虚拟环境的核心作用是隔离不同项目的依赖版本。今天这个项目需要Flask 2.x,另一个项目可能需要Flask 1.x,同台机器上共存容易互相干扰。命令行操作非常清晰:
# Windows下创建虚拟环境 python -m venv venv # 激活虚拟环境(Windows) venv\Scripts\activate # 激活虚拟环境(macOS/Linux) source venv/bin/activate # 安装依赖 pip install flask flask-sqlalchemy pymysql redis flask-wtf安装依赖之后,用pip freeze > requirements.txt把包版本列表导出到文件里。这个步骤很多同学不重视,但毕设说明书里、答辩演示时,老师如果问“这个项目怎么部署到别的机器上”,你会因为这一行命令加不少分。
2.2 Windows下MySQL和Redis安装的几个关键坑
先说MySQL。别去官网下载最新的8.x安装包然后无脑“Next”到底。这里有两个容易踩的坑:
- 端口冲突:默认3306端口有可能被本机旧版MySQL或者其他服务占用,安装时指定端口后要确认防火墙放行。
- 认证插件兼容性:MySQL 8.x默认的认证方式是
caching_sha2_password,老版本Python库(比如未更新的PyMySQL)连接时会报“Authentication plugin 'caching_sha2_password' cannot be loaded”错误。解决方案有两个,一是升级PyMySQL到1.0+,二是在MySQL里给项目专用账号指定mysql_native_password认证:
CREATE USER 'shop_user'@'localhost' IDENTIFIED WITH mysql_native_password BY '你的密码'; GRANT ALL PRIVILEGES ON shop_db.* TO 'shop_user'@'localhost'; FLUSH PRIVILEGES;再说Redis。Windows官方不出Redis,微软曾经维护过一个移植版,只到3.x。现在最推荐的做法是用tporadowski的Redis for Windows分支(基于5.0版本)或者直接在Windows上装WSL后跑Linux原生版本。我个人测试下来,tporadowski那个release版本在Windows 10/11上跑得很稳,解压之后直接跑redis-server.exe就能启动,默认端口6379,没有任何多余配置。
=== 小说明 ===
如果你的机器上同时开了MySQL和Redis,注意两者默认端口不冲突,但Redis默认配置是只监听本机127.0.0.1,所以项目里连接Redis时host就用localhost,别写别的IP。
=== 小说明 ===
2.3 PyCharm里的项目解释器和数据库面板配置
用PyCharm打开项目后,第一件事是设置项目解释器为刚才创建好的虚拟环境。操作路径是File -> Settings -> Project -> Python Interpreter -> Add Interpreter -> Existing,找到venv目录下的python.exe。这一步不设置对,后面运行项目报ModuleNotFoundError,其实不是代码问题,而是解释器选错了全局环境。
PyCharm右边栏自带Database面板,配置好MySQL连接后,可以在IDE里直接执行SQL、查看表结构、甚至可视化编辑数据。开发调试的时候非常方便,不用在Navicat和PyCharm之间来回切换。连接配置填Host为localhost、Port为3306、Database为shop_db,记得先测试连接。
3. 核心功能模块的实现与关键代码解析
3.1 基于Redis的Session和验证码实现
用户登录后,用Flask-Session扩展把session存储到Redis中。这个方案的底气在于Redis天然支持过期时间,用户30分钟不操作自动退出登录,很符合二手商城这种场景。核心配置基本在config.py里:
SECRET_KEY = 'your-secret-key' SESSION_TYPE = 'redis' SESSION_REDIS = redis.from_url('redis://localhost:6379/0') SESSION_PERMANENT = False SESSION_USE_SIGNER = True SESSION_KEY_PREFIX = 'shop:session:'登录验证码也是典型的Redis应用场景。生成验证码图片后,把验证码明文存到Redis,key设置为captcha:用户标识,有效期5分钟,校验的时候取出对比,用完立即删除。这样做比存数据库、存文件都方便,因为Redis自带过期清理机制,不会越积越多。从实操角度给大家一个建议,用Flask原生生成的验证码图片其实不够美观,可以用captcha这个库,兼容性好、生成速度快,几行代码搞定。
3.2 商品发布与图片上传的落地方案
二手商品有个特点:图片往往比文字描述更能说明问题。商品发布模块里,图片上传是体验的关键。这里给大家一个建议:不要把图片存到MySQL数据库里。图片是二进制文件,往数据库里塞两个方案:一是存BLOB字段,二是存文件路径,推荐后者。文件路径方案更灵活,可以把图片放在Flask的static/uploads目录下,服务器不会因为数据库备份而变得巨大。代码层面要注意限制上传文件的格式和大小:
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif', 'webp'} MAX_CONTENT_LENGTH = 5 * 1024 * 1024 # 限制单个文件不超过5MB def allowed_file(filename): return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS另外,给上传的文件改名时千万别直接用用户原始文件名,防止路径穿越攻击(比如文件名里带../)。比较稳妥的做法是用uuid.uuid4().hex生成随机文件名,再附加原扩展名。
3.3 Flask-SQLAlchemy模型设计与表关系
这个项目一共需要四张核心表:用户表、商品表、订单表、交易评价表。如果做购物车功能,还需要一张购物车表。用Flask-SQLAlchemy定义模型,代码直观易读:
class User(db.Model): __tablename__ = 'user' id = db.Column(db.Integer, primary_key=True) username = db.Column(db.String(64), unique=True, nullable=False) password_hash = db.Column(db.String(128), nullable=False) email = db.Column(db.String(128)) avatar_url = db.Column(db.String(256)) create_time = db.Column(db.DateTime, default=datetime.now) # 关联关系:一个用户可发布多个商品 items = db.relationship('Item', backref='owner', lazy='dynamic') class Item(db.Model): __tablename__ = 'item' id = db.Column(db.Integer, primary_key=True) title = db.Column(db.String(100), nullable=False) description = db.Column(db.Text) price = db.Column(db.Numeric(10, 2), nullable=False) # 结合Numeric避免浮点误差 category = db.Column(db.String(50)) image_url = db.Column(db.String(256)) status = db.Column(db.SmallInteger, default=1) # 1:在售 0:已下架 2:已售出 seller_id = db.Column(db.Integer, db.ForeignKey('user.id')) create_time = db.Column(db.DateTime, default=datetime.now) view_count = db.Column(db.Integer, default=0)很多同学在模型设计阶段容易忽略db.Numeric(10, 2)而用db.Float来存价格。这里要说清楚:Float是浮点数,比如0.1 + 0.2会出现0.30000000000000004这种经典问题。商品价格涉及钱的计算,必须用Decimal类型,这个习惯趁早养成,等以后对接支付接口你就知道这个选择有多重要。
3.4 购物车实现:为什么不感性直接用数据库表
购物车是个典型的“高并发写入、低一致性要求”场景,用户加购物车、改数量、删商品,操作非常频繁,但一个用户最终只会下单一次,购物车里的数据即便临时丢失也不产生损失。因此,购物车用Redis的Hash类型存储是最合理的方案,key为cart:{user_id},field为商品ID,value为数量。用Hash类型的好处:
- 一个key里存多个商品,结构清晰
- 修改数量用
HINCRBY,原子操作不会有并发问题 - 查询用
HGETALL,一次拿到全部购物车商品
def add_to_cart(user_id, item_id, quantity=1): cart_key = f"cart:{user_id}" redis_client.hincrby(cart_key, item_id, quantity) def get_cart_items(user_id): cart_key = f"cart:{user_id}" items = redis_client.hgetall(cart_key) # items 是 {item_id: quantity} 的字节字典,解码后去MySQL查商品详情这里要明白一个设计边界:商品本身的标题、价格、图片URL等详细信息还是得从MySQL查,因为Redis里存的只是ID和数量,不保存商品完整信息。这样做的好处是,商家的商品信息改了(比如价格调整),用户的购物车能实时拿到最新价格,不会出现Redis里的数据过期不更新问题。
4. 开发过程中最常踩的坑与排查心得
4.1 SQLAlchemy连不上MySQL、报Public Key Retrieval错误
用的是PyMySQL作为MySQL驱动时,连接串需要额外配置:
SQLALCHEMY_DATABASE_URI = ( "mysql+pymysql://shop_user:密码@localhost:3306/shop_db" "?charset=utf8mb4" )如果使用MySQL 8.0以上版本,连接时报Public Key Retrieval is not allowed错误,解决方案是连接串里追加allowPublicKeyRetrieval=True参数:
"mysql+pymysql://shop_user:密码@localhost:3306/shop_db?charset=utf8mb4&allowPublicKeyRetrieval=True"这个错误在MySQL 8.0.3之后的版本中常见,因为默认的caching_sha2_password插件要求客户端必须通过SSL或者RSA公钥加密来传输密码。本地开发阶段追加这个参数即可。
4.2 Redis连接被拒或者Keys看不懂
如果启动项目时提示ConnectionRefusedError: Error while reading line from the server,先检查Redis服务是否启动。Windows跑到redis-server.exe后那个窗口不要关。还要注意点:Redis默认配置是bind 127.0.0.1和protected-mode yes,如果你的PyCharm里用的连接host写的是局域网IP而不是localhost,连接会被拒绝。
另外提一句,生产环境部署时,Redis一定要设置密码并修改默认端口。实操过程中我见过不少刚上手的朋友,Redis裸奔暴露在公网上,被扫描工具盯上后写入恶意定时任务,整台服务器被挖矿程序占满。本地跑毕设可以不设密码,但安全意识要建立起来。
4.3 循环导入与Flask上下文问题
一个典型的Flask新手错误:在models.py中直接写from app import db,而app/__init__.py中又先导入models再初始化db,两个文件相互引用,导致ImportError: cannot import name 'db' from partially initialized module。
正确的解法是用Flask的应用工厂模式。在app/__init__.py中先创建db对象但不绑定应用,然后在create_app()函数中再初始化:
from flask import Flask from flask_sqlalchemy import SQLAlchemy db = SQLAlchemy() def create_app(): app = Flask(__name__) app.config.from_object('config.Config') db.init_app(app) from .blueprints.auth import auth_bp from .blueprints.item import item_bp # 注册蓝图... return app然后在run.py中:
from app import create_app app = create_app() if __name__ == '__main__': app.run(debug=True)这样就在代码层面避免循环导入。此外还有一类坑是RuntimeError: Working outside of application context,出现在创建数据表的代码上。处理的办法是把建表操作放到应用上下文中执行:
with app.app_context(): db.create_all()4.4 session失效和CSRF校验问题
用Flask-WTF做CSRF防护时,登录表单会出现一个经典的“明明填了验证码为啥还报错”的困惑。这就是session存储方式改变造成的:Flask-WTF默认从session中读取CSRF token进行校验,session配置改成Redis后需要确保SESSION_TYPE配置正确。
使用Redis存session后,调试时看不到session内容,总是不放心。这里给一个调试技巧:用Redis Desktop Manager连接本机Redis,过滤前缀shop:session就能看到所有会话数据。这个工具早期叫RedisDesktopManager,现在新版改名了但功能一样直观。
4.5 部署运行后页面样式丢失
开发阶段Flask会自动处理static目录下的静态文件,但如果项目是直接用python run.py启动、且Flask(__name__)构造时没有指定static_folder参数,有时候会碰见CSS和JS加载404。最常见的解决方案是显式指定:
app = Flask(__name__, static_folder='static', static_url_path='/static')同时模板中引用静态资源要用url_for('static', filename='css/style.css')的方式,不要硬编码相对路径。
5. 项目优化方向:如何让毕设从合格变优秀
5.1 增加Redis缓存商品列表
一个比较出彩的优化,是在读多写少的商品列表接口上加入Redis缓存。当有用户访问首页时,先去Redis查有没有缓存,有则直接返回,没有则查MySQL并写入缓存,设置60秒过期。这样MySQL的查询压力会大大降低,答辩的时候可以顺势说出“我通过缓存减少了数据库压力”这一亮点。
但要注意缓存与数据库数据一致性问题:商家修改商品信息后,需要主动删除对应缓存。如果只是简单设置过期时间,极端情况下用户会看到旧数据,属于“最终一致”,在毕设答辩的完整度上够用。但如果追求更好的效果,可以在发布或修改商品的函数里顺手执行redis_client.delete('item_detail:{}'.format(item_id)),保证修改后下一次请求拿到的是最新数据。
5.2 用RESTful接口规范前后端分离
如果项目答辩要演示接口测试,建议把路由设计成RESTful风格:
POST /api/auth/login:登录POST /api/item:发布商品GET /api/item/<id>:商品详情POST /api/order:创建订单
接口统一返回JSON格式数据,在响应中指定code、message、data三个字段,前端拿到数据再渲染页面。这样做相比Flask渲染Jinja2模板,多了一层前后端关联关系的理解,但更接近企业真实的开发方式,面试聊起来也有话说。
5.3 订单事务处理和并发安全
创建订单时涉及多个操作:扣减商品状态(把在售改为已售)、生成订单记录、清空购物车中对应商品。这三个操作必须在一个数据库事务中完成,要么全部成功,要么全部回滚。Flask-SQLAlchemy中实现事务的方式用起来不难:
try: item.status = 2 db.session.add(order) db.session.commit() except Exception: db.session.rollback() raise关键点在于commit()之前任何一步出错,都要rollback(),否则可能会产生脏数据。在高并发场景下,这里还需要配合乐观锁或悲观锁,但因为毕设项目不会有真实并发量,事务的正常使用已经足够展示功底。
6. 写在最后的实际操作建议
如果你打算拿这套项目直接作为毕设题目或者改造升级,我个人有几个建议。第一,先把项目完整跑起来,把里边的表结构、路由设计整体过一遍,再动手改代码。不建议上来就大改,先小步快跑迭代。第二,务必把README写清楚,包含环境要求、运行步骤、演示账号、功能清单,这一步在毕设报告和答辩时都会用到。第三,如果时间有余力,给项目配上Docker部署脚本,用Docker Compose把MySQL、Redis、Flask三个服务一键编排启动,不仅本地开发方便,还能展示你的工程化能力。
二手交易商城这个选题,本质上是个标准的CRUD项目,但它覆盖的知识面很广——从Web框架到关系型数据库、缓存数据库、用户认证、文件上传、前后端交互,该有的知识点都有,做好做完整的难度恰好卡在本科毕设的合理区间。真正用心做完的同学,能把这套东西吃得透透的,答辩的时候老师随便往哪个方向问都能接住。这比网上随便抄一个复杂的分布式系统然后讲不出所以然,不知道强到哪里去了。
本文还有配套的精品资源,点击获取