首页/新闻资讯/正文详情

收货地址管理|Flask电商毕设,省市区联动+默认地址+下单快照

发布时间:2026/11/7 15:46:03 来源:云帆数科 栏目:资讯中心
收货地址管理|Flask电商毕设,省市区联动+默认地址+下单快照
⚠️免责声明本文项目仅供技术学习参考严禁直接作为毕业设计或课程作业提交。请结合自身需求进行二次开发与创新学术诚信从我做起 先把三个坑说在前面收货地址很多人以为就是个增删改查。建张表存收货人、电话、地址然后CRUD四件套收工。真做下来会发现翻车的地方不在CRUD在三个细节省市区怎么存、默认地址怎么保证只有一个、下单时地址要不要存快照。前两个是常规题。第三个——下单地址快照——是很多人到最后答辩被老师问住的地方。这篇把这三个都说清楚。一、数据表设计先看表。CREATE TABLE address ( id int NOT NULL AUTO_INCREMENT, user_id int NOT NULL COMMENT 所属用户, receiver varchar(50) NOT NULL COMMENT 收货人, phone varchar(20) NOT NULL COMMENT 手机号, province varchar(50) NOT NULL COMMENT 省, city varchar(50) NOT NULL COMMENT 市, district varchar(50) NOT NULL COMMENT 区/县, detail varchar(200) NOT NULL COMMENT 详细地址, is_default tinyint DEFAULT 0 COMMENT 是否默认0否1是, created_at datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_user (user_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;几个点。省市区拆三个字段别合成一个。有人图省事直接存一个广东省深圳市南山区的字符串。能跑但以后想做按地区统计订单就抓瞎了。拆开存扩展性强。detail 只存详细地址街道、门牌号别把省市区再重复存一遍。is_default 用 0/1不用布尔类型跨数据库兼容性好。user_id 建索引因为每次查地址列表都按用户查。补充提示演示毕设没有加updated_at更新时间字段如果是商用项目建议补上方便追踪地址修改记录。二、新增地址import re app.route(/address/add, methods[GET, POST]) login_required def address_add(): if request.method POST: receiver request.form.get(receiver, ).strip() phone request.form.get(phone, ).strip() province request.form.get(province, ).strip() city request.form.get(city, ).strip() district request.form.get(district, ).strip() detail request.form.get(detail, ).strip() is_default 1 if request.form.get(is_default) else 0 # 基础校验 if not all([receiver, phone, province, city, district, detail]): flash(请把地址信息填完整, error) return redirect(url_for(address_add)) # 手机号格式校验 if not re.match(r^1[3-9]\d{9}$, phone): flash(手机号格式不对, error) return redirect(url_for(address_add)) # 第一个地址自动设为默认 count Address.query.filter_by(user_idcurrent_user.id).count() if count 0: is_default 1 # 设了默认先把其他的默认取消 if is_default: Address.query.filter_by(user_idcurrent_user.id).update({is_default: 0}) addr Address( user_idcurrent_user.id, receiverreceiver, phonephone, provinceprovince, citycity, districtdistrict, detaildetail, is_defaultis_default ) db.session.add(addr) db.session.commit() flash(地址添加成功, success) return redirect(url_for(address_list)) return render_template(address/form.html)两个小逻辑值得说。第一个地址自动设为默认。用户加第一个地址时不用给他设为默认的选项——因为只有一个它就是默认。设默认时先清空其他默认。保证一个用户永远只有一个默认地址。⚠️代码短板提醒上面为了教程简洁省略了try‑except事务捕获。毕设演示可以正常运行实际项目需要增加异常捕获与db.session.rollback()回滚防止数据库中途报错造成部分数据更新。三、默认地址怎么保证只有一个这是最容易写错的地方。错误写法# ❌ 只改当前这条其他还是默认会出现多个默认地址 addr.is_default 1 db.session.commit()正确写法——先把该用户所有地址的 is_default 清零再设当前这条def set_default_address(address_id, user_id): 设置默认地址先全部清零再单独设置 Address.query.filter_by(user_iduser_id).update({is_default: 0}) addr Address.query.filter_by(idaddress_id, user_iduser_id).first() if addr: addr.is_default 1 db.session.commit()注意filter_by(user_iduser_id)这个条件不能漏。漏了会怎样A用户能改B用户的默认地址。这是越权。所以记住一条所有涉及地址的操作都要带上 user_id 过滤别只按 id 查。效果长这样——两条地址只有一条标着默认四、省市区联动前端三级联动思路很简单省变了市的选项跟着变市变了区跟着变。数据源一般是一份 JSON省市区数据。// 简化版用省市区数据字典 const regionData { 广东省: { 深圳市: [南山区, 福田区, 宝安区], 广州市: [天河区, 越秀区, 海珠区] }, // ... }; function loadProvince() { const sel document.getElementById(province); for (let p in regionData) { sel.add(new Option(p, p)); } } function loadCity() { const province document.getElementById(province).value; const citySel document.getElementById(city); citySel.length 1; // 保留请选择 document.getElementById(district).length 1; // 区也重置 if (province regionData[province]) { for (let c in regionData[province]) { citySel.add(new Option(c, c)); } } } // district 区县加载逻辑同理真正的省市区数据有几百KB毕设直接用现成的 JSON 文件网上一搜就有不用自己手写。一个坑编辑地址时页面加载完要先手动触发一次联动把省市区回填上不然下拉框是空的。别问我是怎么知道的。实际跑起来的表单长这样省、市、区县三个下拉框选省份下拉框展开露出可选省份选完省城市跟着联动带出注意前端联动只负责UI展示不能信任前端提交过来的省市区参数后端至少要做非空校验商用系统还需要校验行政区域合法性。五、下单时的地址快照重点前面都是常规操作。这一节是重点。先看一个场景。用户10月1号下单收货地址是深圳市南山区科技园1号。10月5号他搬家了在个人中心把地址改成了深圳市福田区XX路2号。现在问题来了他10月1号那个订单收货地址应该显示哪个如果订单表里只存了一个address_id指向地址表——那么地址一改历史订单的地址也跟着变了。用户点开10月1号的订单看到的是新家的地址。这是错的。历史订单要还原的是下单那一刻的信息不是现在的信息。正确做法订单表里存地址快照。CREATE TABLE orders ( ... -- 可以保留address_id当做参考业务展示优先使用快照字段 receiver_name varchar(50) NOT NULL COMMENT 收货人快照, receiver_phone varchar(20) NOT NULL COMMENT 手机号快照, receiver_address varchar(300) NOT NULL COMMENT 完整收货地址快照, ... );下单时把地址拍一张照塞进订单里app.route(/order/create, methods[POST]) login_required def order_create(): address_id request.form.get(address_id, typeint) # 取地址用户选了就用选的没选就用默认的 if address_id: address Address.query.filter_by(idaddress_id, user_idcurrent_user.id).first() else: address Address.query.filter_by(user_idcurrent_user.id, is_default1).first() if not address: flash(请先添加收货地址, error) return redirect(url_for(cart)) # 拼成完整地址 full_address f{address.province}{address.city}{address.district}{address.detail} order Order( user_idcurrent_user.id, # 地址快照——存进去就固定了之后地址怎么改都不影响这一单 receiver_nameaddress.receiver, receiver_phoneaddress.phone, receiver_addressfull_address, ... ) db.session.add(order) db.session.commit()下单后进订单详情收货人和地址就是下单那一刻的快照之后改地址、删地址都不影响这单为什么这么做因为订单是历史凭证。它记录的是当时发生了什么不是现在是什么样。同一个道理订单明细表里也要存商品快照——商品名、图片、单价。这样商品下架了、改名了、涨价了历史订单也不会变。这也解释了另一个问题地址可以被删但订单不受影响——因为订单里已经存了完整信息根本不依赖地址表。这一点答辩很爱问记住。六、补充地址编辑功能实现前面实现了新增、删除地址实际项目还需要编辑地址功能。编辑接口很容易踩两个坑越权访问、修改默认地址逻辑处理。逻辑注意点查询地址必须同时校验id和user_id防止用户篡改id参数读取、修改他人地址。只有用户本次勾选“设为默认地址”时才执行清空全部默认标记不勾选则保留这条地址原本的默认状态不要随意覆盖。编辑页面复用新增地址的模板回显旧地址数据。前端省市区下拉框回显时页面加载完要手动触发联动函数否则下拉框数据为空。app.route(/address/edit/int:address_id, methods[GET, POST]) login_required def address_edit(address_id): # 查询地址必须带上user_id越权防护 addr Address.query.filter_by(idaddress_id, user_idcurrent_user.id).first() if not addr: flash(地址不存在或无权操作, error) return redirect(url_for(address_list)) if request.method POST: receiver request.form.get(receiver, ).strip() phone request.form.get(phone, ).strip() province request.form.get(province, ).strip() city request.form.get(city, ).strip() district request.form.get(district, ).strip() detail request.form.get(detail, ).strip() is_default 1 if request.form.get(is_default) else 0 # 基础非空校验 if not all([receiver, phone, province, city, district, detail]): flash(请把地址信息填完整, error) return render_template(address/form.html, addraddr) # 手机号正则校验 if not re.match(r^1[3-9]\d{9}$, phone): flash(手机号格式不对, error) return render_template(address/form.html, addraddr) # 如果本次编辑勾选设为默认先清空该用户所有默认地址 if is_default: Address.query.filter_by(user_idcurrent_user.id).update({is_default: 0}) addr.is_default 1 # 更新普通字段 addr.receiver receiver addr.phone phone addr.province province addr.city city addr.district district addr.detail detail db.session.commit() flash(地址修改成功, success) return redirect(url_for(address_list)) # GET请求渲染表单回显原有地址 return render_template(address/form.html, addraddr)⚠️代码为教程演示简化版本生产环境建议加上 try‑except rollback 数据库事务处理。❌很多同学写编辑时的错误示范# ❌错误写法不管勾不勾选默认直接覆盖is_default addr.is_default is_default问题用户编辑地址没有勾选“设为默认”会把原本的默认地址直接变成非默认业务bug。✅正确只有is_default等于1的时候才去修改默认状态。form.html 复用表单模板参考!-- form.html 新增/编辑复用表单 -- form methodpost div label收货人/label input typetext namereceiver value{{ addr.receiver if addr else }} required /div div label手机号/label input typetext namephone value{{ addr.phone if addr else }} required /div div select idprovince nameprovince required option value请选择省份/option /select /div div select idcity namecity required option value请选择城市/option /select /div div select iddistrict namedistrict required option value请选择区县/option /select /div div label详细地址/label input typetext namedetail value{{ addr.detail if addr else }} required /div div input typecheckbox nameis_default value1 {% if addr and addr.is_default %}checked{% endif %} label设为默认地址/label /div button typesubmit保存/button /form {% if addr %} !-- 编辑模式页面加载完成回填省市区 -- script window.onload function(){ // 给下拉框赋值再触发联动 document.getElementById(province).value{{ addr.province }}; loadCity(); document.getElementById(city).value{{ addr.city }}; // 此处自行补充加载区县的函数调用 } /script {% endif %}编辑回显效果——省市区下拉框自动回填默认勾选框也带出来七、删除地址app.route(/address/delete/int:address_id, methods[POST]) login_required def address_delete(address_id): # 一定要带 user_id防止删别人的地址 addr Address.query.filter_by(idaddress_id, user_idcurrent_user.id).first() if not addr: flash(地址不存在, error) return redirect(url_for(address_list)) was_default addr.is_default db.session.delete(addr) db.session.commit() # 如果删掉的是默认地址把剩下最新的一个补为默认 if was_default: first Address.query.filter_by(user_idcurrent_user.id)\ .order_by(Address.created_at.desc()).first() if first: first.is_default 1 db.session.commit() flash(地址已删除, success) return redirect(url_for(address_list))注意最后那段删掉默认地址后要补一个默认上去。不然用户下次下单、没主动选地址时系统就找不到默认地址了。边界业务处理删除前校验地址归属不能删除其他用户地址如果被删除的是默认地址则自动将剩余地址中创建时间最新的地址设置为新默认特殊情况当用户只有一条地址删除后该用户没有任何地址不会生成默认下单页面会提示用户新增地址。这种边界情况就是毕设和能跑就行的分水岭。地址这块答辩爱问这几问1. 为什么下单要存地址快照不直接关联地址表答订单是历史凭证。用户之后可能修改或删除地址如果订单只存 address_id历史订单的收货信息就会跟着变这是错的。存快照能保证订单记录的是下单那一刻的真实信息。同理订单明细里也要存商品快照。2. 一个用户能有几个默认地址答只能有一个。设置默认时先把该用户所有地址的 is_default 清零再设置目标地址。另外所有地址操作都要带上 user_id 条件防止越权。3. 省市区为什么要拆成三个字段答方便按地区做统计和筛选。如果合成一个字符串以后想做广东省的订单有多少就得做字符串匹配效率和准确性都差。4. 用户删除地址后历史订单会受影响吗答不会。订单里存的是地址快照不依赖地址表。地址删了订单信息依然完整。收货地址这块代码量不大但藏着几个容易踩的专业坑。省市区怎么存、默认地址怎么保唯一、下单要不要快照——这三关一过个人中心这块基本就稳了。尤其是地址快照这个点它背后是电商系统的一条基本设计原则订单记录的是历史不是现状。想明白这一条购物车、订单、售后那些地方很多设计就通了——这也是我做完这套最实在的体会。梅雅达编程笔记原创 · 首发 CSDN · 转载请注明出处

相关推荐

松江游玩记录
松江游玩记录

Mix Shakalaka鲨卡美式大老远来松江,就为这一口《东京大饭店》 不得不说,为了这顿饭我横跨半个上海冲到松江!这家开了五年的老店「鲨卡」简直是剧粉的圆梦现场,《东京大饭店》里的感觉直接拉满🥩1kg美式果木慢烤牛肋排… · 2026/11/5 16:40:06

【AI大模型接入SDK】ChatServer接口测试
【AI大模型接入SDK】ChatServer接口测试

🎬 个人主页:艾莉丝努力练剑❄专栏传送门:《C语言》《数据结构与算法》《C/C干货分享&学习过程记录》 《Linux操作系统编程详解》《笔试/面试常见算法:从基础到进阶》《Python干货分享》⭐️为天地立心,为生民立命… · 2026/11/7 15:46:03

DSH 插件调试避坑:client.js 设置面板、热重载与高频报错排查表
DSH 插件调试避坑:client.js 设置面板、热重载与高频报错排查表

CFzjy AI Engineer ▍深耕领域 AI Agent 架构设计 Harness 插件开发 Context Engineering 多智能体协同 & RAG ▍技术底座 Python TypeScript Cordis 插件 MCP & Skills FastAPI Docker ▍工程能力 专注 Agent 运行时与 Harness 插件开发、长上下文与流式… · 2026/11/7 7:39:51

电工证换证找谁千万别踩坑
电工证换证找谁千万别踩坑

电工证换证找谁千万别踩坑 之前考的证在外省能不能转过来?这个问题困扰着不少在外地打工、换工作、甚至想返乡发展的电工朋友们。特别是在平顶山从事公路工程的电工,很多都是外地人,手里有证但不确定能不能在当地顺利换证,担心被坑、被套路、被拒绝。别急… · 2026/11/7 15:45:38

电工没有电工证怎么办?官方回应来了,别再被坑
电工没有电工证怎么办?官方回应来了,别再被坑

电工没有电工证怎么办?官方回应来了,别再被坑 证书过期了不知道怎么复审延期,这个问题在电工圈子里已经不是什么新鲜事了。很多人以为电工证只是个“纸老虎”,但一旦被查到无证上岗,轻则罚款,重则停工整顿。官方回应中也明确表示,特种作业操作证是从事… · 2026/11/7 15:45:38

考电工证有专业要求吗 通过率揭秘你知道吗
考电工证有专业要求吗 通过率揭秘你知道吗

考电工证有专业要求吗 通过率揭秘你知道吗 之前考的证在外省能不能转过来?这几乎是每个想考电工证的小伙伴最先关心的问题。别急,今天就来给你讲清楚,考电工证到底有没有专业要求,还有你最关心的通过率到底是多少,以及怎么把证从外省转过来。… · 2026/11/7 15:45:38

有电工证什么都不懂怎么报名也能考过?三门峡建筑工人的亲身经历
有电工证什么都不懂怎么报名也能考过?三门峡建筑工人的亲身经历

有电工证什么都不懂怎么报名也能考过?三门峡建筑工人的亲身经历 你是不是也这样?拿着电工证,什么都不懂,怕报不上名,怕考试不过,怕报名流程太复杂?其实,你不是一个人。三门峡的张师傅就是个典型例子,他学历不高,但靠着一步步摸索,最后顺利考过了低… · 2026/11/7 15:45:38

顺义哪能考电工证?良心建议告诉你怎么操作更省事
顺义哪能考电工证?良心建议告诉你怎么操作更省事

顺义哪能考电工证?良心建议告诉你怎么操作更省事 之前考的证在外省能不能转过来?你不是一个人,很多人都遇到过这个问题。今天咱就从 顺义哪能考电工证 这个角度,聊聊怎么把外省证转过来,怎么在当地报名,以及考了证之后的 薪资前景… · 2026/11/7 15:45:38

焦作温县电工证千万别踩坑,外省证能转过来吗?
焦作温县电工证千万别踩坑,外省证能转过来吗?

焦作温县电工证千万别踩坑,外省证能转过来吗? 之前考的证在外省能不能转过来?这是很多从业者最关心的问题,尤其是在焦作温县这样电工证需求量大的地方,很多人因为在外省考的证无法顺利转过来而白白浪费时间和金钱, 千万别踩坑… · 2026/11/7 15:44:38

双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/7 11:25:08

STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/7 12:02:18

DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/6 10:29:23

澳洲水电工证考试:证书过期了不知道怎么复审延期?电子证书怎么弄?
澳洲水电工证考试:证书过期了不知道怎么复审延期?电子证书怎么弄?

澳洲水电工证考试:证书过期了不知道怎么复审延期?电子证书怎么弄? 证书过期了不知道怎么复审延期?别慌,很多人在拿到【澳洲水电工证考试】的电子证书后,一不小心就忽略了复审时间,结果证书失效,工作受阻,甚至被单位追责。今天就从【澳洲水电工证考试… · 2026/11/7 0:00:41

电工作业提示版通过率揭秘:怎么考才能一次过?
电工作业提示版通过率揭秘:怎么考才能一次过?

电工作业提示版通过率揭秘:怎么考才能一次过? 怕考不过白交培训费?别急,今天我就从驻马店应届工程类毕业生的视角,带你全面了解电工作业提示版的考试流程、备考要点和真实通过率,帮你少走弯路、提高一次通过率。… · 2026/11/7 0:00:41

电工证年审报名千万别踩坑 外省证转过来要搞清这几点
电工证年审报名千万别踩坑 外省证转过来要搞清这几点

电工证年审报名千万别踩坑 外省证转过来要搞清这几点 之前考的证在外省能不能转过来?这个问题是很多电工从业者最关心的,特别是像你这样在商丘从事公路工程的人员,经常需要跨省流动,证书的 有效性 和 可迁移性… · 2026/11/7 0:00:41

双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/7 11:25:08

STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/7 12:02:18

DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/6 10:29:23

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码