2026/9/9 1:04:14

Flask开发二手商城实战:从技术选型到核心模块落地

Flask开发二手商城实战:从技术选型到核心模块落地 简介基于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_userlocalhost IDENTIFIED WITH mysql_native_password BY 你的密码; GRANT ALL PRIVILEGES ON shop_db.* TO shop_userlocalhost; 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应用场景。生成验证码图片后把验证码明文存到Rediskey设置为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_keyTrue) username db.Column(db.String(64), uniqueTrue, nullableFalse) password_hash db.Column(db.String(128), nullableFalse) email db.Column(db.String(128)) avatar_url db.Column(db.String(256)) create_time db.Column(db.DateTime, defaultdatetime.now) # 关联关系一个用户可发布多个商品 items db.relationship(Item, backrefowner, lazydynamic) class Item(db.Model): __tablename__ item id db.Column(db.Integer, primary_keyTrue) title db.Column(db.String(100), nullableFalse) description db.Column(db.Text) price db.Column(db.Numeric(10, 2), nullableFalse) # 结合Numeric避免浮点误差 category db.Column(db.String(50)) image_url db.Column(db.String(256)) status db.Column(db.SmallInteger, default1) # 1:在售 0:已下架 2:已售出 seller_id db.Column(db.Integer, db.ForeignKey(user.id)) create_time db.Column(db.DateTime, defaultdatetime.now) view_count db.Column(db.Integer, default0)很多同学在模型设计阶段容易忽略db.Numeric(10, 2)而用db.Float来存价格。这里要说清楚Float是浮点数比如0.1 0.2会出现0.30000000000000004这种经典问题。商品价格涉及钱的计算必须用Decimal类型这个习惯趁早养成等以后对接支付接口你就知道这个选择有多重要。3.4 购物车实现为什么不感性直接用数据库表购物车是个典型的“高并发写入、低一致性要求”场景用户加购物车、改数量、删商品操作非常频繁但一个用户最终只会下单一次购物车里的数据即便临时丢失也不产生损失。因此购物车用Redis的Hash类型存储是最合理的方案key为cart:{user_id}field为商品IDvalue为数量。用Hash类型的好处一个key里存多个商品结构清晰修改数量用HINCRBY原子操作不会有并发问题查询用HGETALL一次拿到全部购物车商品def add_to_cart(user_id, item_id, quantity1): cart_key fcart:{user_id} redis_client.hincrby(cart_key, item_id, quantity) def get_cart_items(user_id): cart_key fcart:{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 ( mysqlpymysql://shop_user:密码localhost:3306/shop_db ?charsetutf8mb4 )如果使用MySQL 8.0以上版本连接时报Public Key Retrieval is not allowed错误解决方案是连接串里追加allowPublicKeyRetrievalTrue参数mysqlpymysql://shop_user:密码localhost:3306/shop_db?charsetutf8mb4allowPublicKeyRetrievalTrue这个错误在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(debugTrue)这样就在代码层面避免循环导入。此外还有一类坑是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_folderstatic, static_url_path/static)同时模板中引用静态资源要用url_for(static, filenamecss/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框架到关系型数据库、缓存数据库、用户认证、文件上传、前后端交互该有的知识点都有做好做完整的难度恰好卡在本科毕设的合理区间。真正用心做完的同学能把这套东西吃得透透的答辩的时候老师随便往哪个方向问都能接住。这比网上随便抄一个复杂的分布式系统然后讲不出所以然不知道强到哪里去了。本文还有配套的精品资源点击获取