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

两天搭出可运行CRM原型:Flask+SQLite实现线索到成交闭环

  • 首页
  • 资讯中心
  • /
  • 两天搭出可运行CRM原型:Flask+SQLite实现线索到成交闭环

相关资讯

IDEA代码补全大小写敏感怎么关?三步配置让提示更智能 2026/10/13 2:40:44
Linux进程信号机制全解析:从原理到多线程实践 2026/10/13 2:40:44
DMA总读旧数据?三招解决Cache一致性难题 2026/10/13 2:40:44

最新资讯

openEuler离线升级包:构建内网等保环境专属软件源与事务回滚实践
德生TSW-F4社保卡读卡器开发指南:从驱动安装到APDU交互实战
VirtualBox 5.2.12 实战:虚拟机安装、网络配置与自动化测试
DataGridView单元格合并方案:基于CellPainting自绘的实现与优化
传输设备调试工具Navigator 6.2使用详解与避坑指南
从爬虫到情感分析:城市评论数据闭环实践指南

今日推荐

ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查
CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析
CAN总线错误处理机制深度解析:从位错误到Bus Off的硬件级调试

本周热门

UE动画修改实战:从资产编辑到重定向与蒙太奇驱动
统计随机数生成器攻击下的KLJN安全密钥交换协议Matlab仿真
政务API安全治理:资产测绘、低代码编排与行标对标实践

本月精选

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证
2026 大模型集体涨价:用 Python 做企业 Token 成本测算与选型避坑(附配置)

两天搭出可运行CRM原型:Flask+SQLite实现线索到成交闭环

发布时间:2026/10/13 2:40:44
两天搭出可运行CRM原型:Flask+SQLite实现线索到成交闭环 简介本资源是一套面向前端开发初学者与毕业设计学生的CRM客户关系管理系统原型页面聚焦于交互逻辑实现与界面体验优化适用于Web前端课程设计、毕设原型开发及JavaScript实战练习。压缩包为RAR格式大小10MB虽未提供具体文件总数与类型明细但根据描述可知其包含完整的HTML页面结构、集成JavaScript表单验证逻辑、基于jQuery的DOM操作与动态效果代码以及采用Ajax技术实现的无刷新数据交互模块覆盖用户注册、客户信息录入、订单状态查询等典型业务场景。已有301人学习下载反映出该原型在教学实践中的实用价值。读者可直接获取一套结构清晰、注释充分、具备响应式设计的CRM前端原型工程掌握表单实时校验、元素动态控制、异步通信及UI动效等核心技能并可基于此快速拓展后端对接或迭代功能模块。1. CRM客户关系管理系统原型不是PPT画布而是能跑通线索→商机→成交闭环的最小可验证体很多团队一说“做CRM原型”第一反应是打开Axure拖几个表单、画几条流程线导出PDF发给业务方签字——结果上线前发现销售根本不会按你设计的“线索分配规则”手动点“转商机”客服也拒绝在“服务满意度字段”里填数字。真正的CRM原型必须是带真实数据流、有状态机驱动、能被销售拿去当天工作台用的轻量级系统。它不追求功能完整但要求每个模块都经得起“今天下午就让A同学用它录3条新线索并走完首次跟进”的压力测试。本文讲的就是如何用PythonFlaskSQLite在两天内搭出这样一个原型支持线索录入、自动打标签、商机阶段推进、基础统计看板所有代码可直接运行数据库结构清晰可改接口留好扩展缝。适合刚接手CRM需求的产品经理、想快速验证流程逻辑的开发工程师以及需要向管理层证明“先跑通再扩功能”可行性的技术负责人。2. 用FlaskSQLite三步搭起CRM核心骨架从零初始化到首页可访问CRM原型不是堆功能而是建骨架。骨架稳了后续加字段、改流程、接短信通知才不翻车。我们选Flask而非Django是因为它不强制你接受ORM、Admin、用户认证整套方案——原型阶段你只需要一个路由、一个模板、一个数据库连接。SQLite则彻底省掉部署数据库服务的步骤.db文件即数据库双击就能用DB Browser打开查数据销售同事提“为什么这条线索没进我的池子”你5秒内就能定位到leads表里assigned_to字段值是不是空。2.1 初始化项目结构与依赖安装新建目录crm-prototype执行以下命令注意Python 3.8mkdir crm-prototype cd crm-prototype python -m venv venv source venv/bin/activate # Windows用 venv\Scripts\activate pip install flask flask-sqlalchemy python-dotenv提示flask-sqlalchemy用于简化数据库操作但不启用其自动迁移功能flask-migrate原型阶段表结构变更是手动SQL更可控python-dotenv用来管理配置避免把DEBUGTrue误提交到代码库。2.2 定义核心数据模型线索、商机、联系人三张表在项目根目录创建models.py定义三个实体。关键点在于状态字段用字符串而非外键——原型阶段业务规则未固化硬编码status ENUM(new,contacted,qualified)反而限制迭代# models.py from flask_sqlalchemy import SQLAlchemy from datetime import datetime db SQLAlchemy() class Lead(db.Model): id db.Column(db.Integer, primary_keyTrue) name db.Column(db.String(100), nullableFalse) phone db.Column(db.String(20)) email db.Column(db.String(120)) source db.Column(db.String(50), defaultweb_form) # 来源web_form, wechat, call status db.Column(db.String(20), defaultnew) # 状态new, contacted, qualified, lost assigned_to db.Column(db.String(50), default) # 分配给谁姓名或ID created_at db.Column(db.DateTime, defaultdatetime.utcnow) updated_at db.Column(db.DateTime, defaultdatetime.utcnow, onupdatedatetime.utcnow) class Opportunity(db.Model): id db.Column(db.Integer, primary_keyTrue) lead_id db.Column(db.Integer, db.ForeignKey(lead.id), nullableFalse) name db.Column(db.String(100), nullableFalse) stage db.Column(db.String(30), defaultprospecting) # 阶段prospecting, proposal, negotiation, closed_won, closed_lost value db.Column(db.Float, default0.0) close_date db.Column(db.Date) created_at db.Column(db.DateTime, defaultdatetime.utcnow) class Contact(db.Model): id db.Column(db.Integer, primary_keyTrue) lead_id db.Column(db.Integer, db.ForeignKey(lead.id)) name db.Column(db.String(100), nullableFalse) position db.Column(db.String(100)) phone db.Column(db.String(20)) email db.Column(db.String(120)) is_primary db.Column(db.Boolean, defaultFalse)逻辑说明Lead是源头所有客户信息起点Opportunity必须关联Lead体现“线索升商机”强业务约束Contact允许一对多一个线索可能有多个对接人。status和stage用字符串而非枚举类型方便后期在前端下拉框里直接增删选项无需改代码、重跑迁移。assigned_to存字符串而非用户ID因原型阶段无统一用户中心销售姓名如“A同学”比UUID更直观业务方一眼能懂。2.3 创建应用入口与数据库初始化脚本创建app.py这是整个原型的启动心脏# app.py from flask import Flask, render_template, request, redirect, url_for, flash from models import db, Lead, Opportunity, Contact import os def create_app(): app Flask(__name__) app.config[SECRET_KEY] os.environ.get(SECRET_KEY, dev-key-change-in-prod) app.config[SQLALCHEMY_DATABASE_URI] sqlite:///crm.db app.config[SQLALCHEMY_TRACK_MODIFICATIONS] False db.init_app(app) # 创建数据库表仅首次运行 with app.app_context(): db.create_all() return app app create_app() app.route(/) def index(): leads Lead.query.order_by(Lead.created_at.desc()).limit(10).all() return render_template(index.html, leadsleads) if __name__ __main__: app.run(debugTrue)参数说明SQLALCHEMY_DATABASE_URI sqlite:///crm.db数据库文件名为crm.db与代码同目录无需额外安装数据库服务。db.create_all()放在with app.app_context():内确保Flask应用上下文已加载否则会报RuntimeError: Working outside of application context。debugTrue仅限本地开发上线前必须设为False并移除SECRET_KEY硬编码用环境变量。运行python app.py访问http://127.0.0.1:5000若看到空白页因index.html尚未创建说明骨架已通电——下一步就是让页面活起来。3. 实现线索全生命周期操作从表单提交到状态流转的闭环逻辑原型的价值在于让业务动作可追踪。销售点“新建线索”填完表单点提交数据进库再点“转商机”自动生成商机记录并更新线索状态最后在列表页看到“阶段”列从new变成qualified——这个闭环就是业务方信任你的起点。本节实现从线索录入、状态更新、商机创建到列表展示的完整链路所有操作均通过原生HTML表单Flask路由完成不引入前端框架降低理解门槛。3.1 构建线索录入表单与后端接收逻辑在项目根目录创建templates文件夹新建templates/lead_form.html!-- templates/lead_form.html -- !DOCTYPE html html headtitle新建线索/title/head body h2新建线索/h2 form methodPOST label姓名input typetext namename required/labelbrbr label电话input typetext namephone/labelbrbr label邮箱input typeemail nameemail/labelbrbr label来源 select namesource option valueweb_form网站表单/option option valuewechat微信咨询/option option valuecall电话呼入/option /select /labelbrbr label分配给input typetext nameassigned_to placeholder例如A同学/labelbrbr button typesubmit保存线索/button /form /body /html在app.py中添加对应路由放在app.route(/)下方app.route(/leads/new, methods[GET, POST]) def new_lead(): if request.method POST: lead Lead( namerequest.form[name], phonerequest.form.get(phone, ), emailrequest.form.get(email, ), sourcerequest.form[source], assigned_torequest.form.get(assigned_to, ) ) db.session.add(lead) db.session.commit() flash(f线索 {lead.name} 创建成功, success) return redirect(url_for(index)) return render_template(lead_form.html)逻辑说明request.form.get(field, )用get而非[]避免字段缺失时报KeyError空字符串作为默认值比None更易在数据库中显示和查询。flash()函数需在index.html中渲染提示消息稍后补充redirect(url_for(index))防止用户刷新页面时重复提交表单PRG模式。此处未做手机号/邮箱格式校验因原型阶段优先保证流程跑通校验逻辑可后续用JavaScript或Flask-WTF补上。3.2 实现线索状态更新与商机创建联动销售跟进线索后需标记状态并可能创建商机。我们在线索列表页index.html为每条线索添加操作按钮并在app.py中新增两个路由# app.py 中追加 app.route(/leads/int:lead_id/update_status, methods[POST]) def update_lead_status(lead_id): lead Lead.query.get_or_404(lead_id) new_status request.form[status] lead.status new_status lead.updated_at datetime.utcnow() # 若状态变为 qualified自动创建商机最小闭环 if new_status qualified: opp Opportunity( lead_idlead_id, namef{lead.name}的商机, stageprospecting ) db.session.add(opp) db.session.commit() flash(f线索 {lead.name} 状态已更新为 {new_status}, info) return redirect(url_for(index)) app.route(/opportunities/int:opp_id/update_stage, methods[POST]) def update_opportunity_stage(opp_id): opp Opportunity.query.get_or_404(opp_id) new_stage request.form[stage] opp.stage new_stage opp.updated_at datetime.utcnow() db.session.commit() flash(f商机阶段已更新为 {new_stage}, info) return redirect(url_for(index))对应templates/index.html需先创建!-- templates/index.html -- !DOCTYPE html html headtitleCRM原型首页/title/head body h1CRM客户关系管理系统原型/h1 !-- Flash消息显示区 -- {% with messages get_flashed_messages(with_categoriestrue) %} {% if messages %} {% for category, message in messages %} div stylecolor: {% if category error %}red{% elif category success %}green{% else %}blue{% endif %}; {{ message }} /div {% endfor %} {% endif %} {% endwith %} a href{{ url_for(new_lead) }} 新建线索/abrbr h2最新线索10条/h2 table border1 classdataframe thead trth姓名/thth电话/thth来源/thth状态/thth分配给/thth操作/th/tr /thead tbody {% for lead in leads %} tr td{{ lead.name }}/td td{{ lead.phone }}/td td{{ lead.source }}/td td{{ lead.status }}/td td{{ lead.assigned_to }}/td td form methodPOST action{{ url_for(update_lead_status, lead_idlead.id) }} styledisplay:inline; select namestatus option valuenew {% if lead.status new %}selected{% endif %}新线索/option option valuecontacted {% if lead.status contacted %}selected{% endif %}已联系/option option valuequalified {% if lead.status qualified %}selected{% endif %}已确认商机/option option valuelost {% if lead.status lost %}selected{% endif %}已流失/option /select button typesubmit更新/button /form /td /tr {% endfor %} /tbody /table h2商机列表/h2 table border1 classdataframe thead trth商机名称/thth关联线索/thth阶段/thth操作/th/tr /thead tbody {% for opp in opportunities %} tr td{{ opp.name }}/td td{{ opp.lead.name }}/td td{{ opp.stage }}/td td form methodPOST action{{ url_for(update_opportunity_stage, opp_idopp.id) }} styledisplay:inline; select namestage option valueprospecting {% if opp.stage prospecting %}selected{% endif %}初步接触/option option valueproposal {% if opp.stage proposal %}selected{% endif %}方案提交/option option valuenegotiation {% if opp.stage negotiation %}selected{% endif %}谈判中/option option valueclosed_won {% if opp.stage closed_won %}selected{% endif %}已成交/option option valueclosed_lost {% if opp.stage closed_lost %}selected{% endif %}已失败/option /select button typesubmit更新阶段/button /form /td /tr {% endfor %} /tbody /table /body /html关键细节url_for(update_lead_status, lead_idlead.id)生成动态URL比硬写/leads/123/update_status更安全路由名改了模板自动适配。表单styledisplay:inline;让下拉框和按钮在同一行UI更紧凑selected属性根据当前状态高亮选项提升操作反馈。商机列表中{{ opp.lead.name }}能直接访问关联线索得益于SQLAlchemy的relationship需在models.py中补上见下文。注意上面模板中用了opportunities变量需在index()路由中查询并传入。修改app.py中的index函数app.route(/) def index(): leads Lead.query.order_by(Lead.created_at.desc()).limit(10).all() opportunities Opportunity.query.join(Lead).order_by(Opportunity.created_at.desc()).limit(10).all() return render_template(index.html, leadsleads, opportunitiesopportunities)3.3 补全模型关系与数据库查询优化当前Opportunity模型缺少对Lead的反向引用导致模板中opp.lead.name报错。在models.py中Opportunity类下方添加关系声明# models.py 中追加 class Opportunity(db.Model): # ... 原有字段 ... lead db.relationship(Lead, backrefdb.backref(opportunities, lazyTrue))同时为提升查询效率避免N1问题在index()路由中使用join一次性查出线索和商机而非循环查# app.py 中 index() 函数内 opportunities Opportunity.query\ .join(Lead, Opportunity.lead_id Lead.id)\ .add_columns(Lead.name.label(lead_name))\ .order_by(Opportunity.created_at.desc())\ .limit(10).all()然后在模板中改为{{ lead_name }}。此写法明确告知SQLAlchemy要JOIN生成的SQL只有一条而非对每条商机再查一次线索。4. 避坑原型阶段最常踩的5个深坑及血泪解决方案做CRM原型不是写Hello World业务逻辑嵌套、状态流转约束、数据一致性要求极高。我曾在某高校实验室带学生做模拟项目X时连续三天卡在同一个问题上销售反馈“线索转商机后列表里还是显示new”。排查发现是db.session.commit()调用位置错误。以下是我在多个CRM原型项目中踩过的真坑按发生频率排序每条都附可复现的代码片段和修复命令。4.1 现象线索状态更新后页面不刷新仍显示旧值原因Flask默认不缓存POST请求但浏览器可能对GET请求如/启用了强缓存导致重定向后看到的是旧页面快照。解决在app.py顶部添加响应头禁用页面缓存仅开发期from functools import wraps from flask import make_response def no_cache(f): wraps(f) def decorated_function(*args, **kwargs): response make_response(f(*args, **kwargs)) response.headers[Cache-Control] no-store, no-cache, must-revalidate, max-age0 return response return decorated_function # 在 app.route(/) 上方添加装饰器 app.route(/) no_cache def index(): # ... 原有代码4.2 现象assigned_to字段存了空格导致销售查不到自己名下的线索原因用户输入 A同学 前后空格未清理WHERE assigned_to A同学匹配失败。解决在new_lead和update_lead_status路由中对字符串字段做strip()# 替换 app.py 中 lead Lead(...) 的赋值部分 lead Lead( namerequest.form[name].strip(), phonerequest.form.get(phone, ).strip(), emailrequest.form.get(email, ).strip(), sourcerequest.form[source].strip(), assigned_torequest.form.get(assigned_to, ).strip() )4.3 现象商机创建后线索状态未自动更新为qualified原因update_lead_status路由中if new_status qualified:判断后创建商机但忘记提交事务db.session.add(opp)后无db.session.commit()商机记录未落库。解决将db.session.commit()移到所有操作之后确保线索和商机原子性提交# 修改 update_lead_status 路由末尾 db.session.commit() # 此行必须在 if 块之外且在所有 add() 之后4.4 现象datetime.utcnow()在不同服务器时区下返回时间不一致导致“今日线索”统计不准原因utcnow()返回UTC时间但销售看报表习惯用本地时间如东八区WHERE created_at 2024-06-01实际查的是UTC时间。解决原型阶段统一用UTC但显示时转换。在模板中用Jinja2过滤器需先注册# app.py 中 create_app() 内添加 app.template_filter(localtime) def localtime(dt): from datetime import timedelta return dt timedelta(hours8) # 东八区偏移模板中使用{{ lead.created_at | localtime }}。4.5 现象SQLite数据库被多个请求同时写入报database is locked错误原因SQLite是文件数据库高并发写入时锁表而原型常开多个浏览器标签测试。解决增加重试机制。在app.py顶部添加import time from sqlalchemy.exc import OperationalError def safe_commit(): for i in range(3): try: db.session.commit() return True except OperationalError: time.sleep(0.1 * (2 ** i)) # 指数退避 return False # 在所有 commit() 处替换为 safe_commit()然后将所有db.session.commit()替换为safe_commit()。5. 加入自动化标签与基础统计看板让原型从“能用”升级为“有用”原型做完线索录入和状态流转只是完成了“能用”。真正让业务方眼前一亮的是系统能主动帮他们发现问题——比如自动给来自“微信咨询”的线索打上high_priority标签或在首页显示“本周新线索23条已转化商机8个”。本节不引入复杂BI工具用纯PythonJinja2实现两件事基于规则的线索自动打标以及首页嵌入的轻量级统计卡片。所有逻辑都在app.py内完成无需额外服务。5.1 实现基于规则的线索自动打标用字典代替硬编码if-else标签Tag是CRM的灵魂它让销售能快速筛选高价值线索。原型阶段不搞机器学习用确定性规则邮箱含company.com→ 标签enterprise电话以138开头 → 标签vip_mobile来源为wechat→ 标签high_touch在models.py中为Lead添加tags字段字符串逗号分隔便于原型期快速增删# models.py 中 Lead 类内追加 tags db.Column(db.String(200), default)在new_lead路由中提交前插入打标逻辑# app.py 中 new_lead() 函数内db.session.add(lead) 之前添加 def apply_tags(lead): tags [] if company.com in (lead.email or ): tags.append(enterprise) if lead.phone and lead.phone.startswith(138): tags.append(vip_mobile) if lead.source wechat: tags.append(high_touch) lead.tags ,.join(tags) # 在 lead Lead(...) 之后db.session.add(lead) 之前调用 apply_tags(lead)逻辑说明tags用逗号分隔而非JSON因SQLite无原生JSON支持字符串更易用SQL查询如WHERE tags LIKE %enterprise%。规则函数apply_tags()独立封装未来可轻松替换为调用外部规则引擎API。不在数据库层面建tags关联表因原型阶段标签数量10个冗余存储比JOIN更高效。5.2 构建首页统计看板5个SQL查询撑起业务仪表盘销售总监第一次打开CRM最想看的不是列表而是“我团队干得怎么样”。我们在index()路由中增加5个统计查询全部用原生SQL绕过ORM性能损耗结果以字典形式传给模板# app.py 中 index() 函数内追加 stats {} # 1. 今日新线索 result db.session.execute(SELECT COUNT(*) FROM lead WHERE DATE(created_at) DATE(now)) stats[today_leads] result.scalar() # 2. 本周转化率线索→商机 result db.session.execute( SELECT COUNT(l.id) as total_leads, COUNT(o.id) as opp_count FROM lead l LEFT JOIN opportunity o ON l.id o.lead_id WHERE l.created_at DATE(now, -7 days) ) row result.fetchone() stats[week_conversion] f{(row.opp_count / row.total_leads * 100):.1f}% if row.total_leads else 0% # 3. 各阶段商机数 result db.session.execute( SELECT stage, COUNT(*) as cnt FROM opportunity GROUP BY stage ) stats[stage_breakdown] {row.stage: row.cnt for row in result} # 4. 高优先级线索数含high_priority标签 result db.session.execute(SELECT COUNT(*) FROM lead WHERE tags LIKE %high_touch%) stats[high_touch_leads] result.scalar() # 5. 待跟进线索状态为new或contacted result db.session.execute(SELECT COUNT(*) FROM lead WHERE status IN (new, contacted)) stats[pending_followup] result.scalar()在templates/index.html中于h1下方插入统计卡片!-- templates/index.html 中 h1 后添加 -- div styledisplay: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 16px; margin: 20px 0; div styleborder: 1px solid #ddd; padding: 12px; text-align: center; div stylefont-size: 18px; font-weight: bold; color: #2c3e50;{{ stats.today_leads }}/div div今日新线索/div /div div styleborder: 1px solid #ddd; padding: 12px; text-align: center; div stylefont-size: 18px; font-weight: bold; color: #27ae60;{{ stats.week_conversion }}/div div本周转化率/div /div div styleborder: 1px solid #ddd; padding: 12px; text-align: center; div stylefont-size: 18px; font-weight: bold; color: #e67e22;{{ stats.high_touch_leads }}/div div高触达线索/div /div div styleborder: 1px solid #ddd; padding: 12px; text-align: center; div stylefont-size: 18px; font-weight: bold; color: #e74c3c;{{ stats.pending_followup }}/div div待跟进线索/div /div /div !-- 阶段分布用表格 -- h3商机阶段分布/h3 table border1 stylewidth:100%; text-align:center; trth阶段/thth数量/th/tr {% for stage, cnt in stats.stage_breakdown.items() %} trtd{{ stage }}/tdtd{{ cnt }}/td/tr {% endfor %} /table提示SQLite的DATE(now)和DATE(now, -7 days)是跨平台安全的日期函数无需担心MySQL或PostgreSQL语法差异。原型阶段用SQLite上线时换数据库只需改SQLALCHEMY_DATABASE_URI统计SQL几乎不用动。5.3 进阶技巧用CSV导出功能让销售自己分析数据业务方常提“能不能把线索导出成Excel”原型阶段不必集成Excel库用CSV即可。在app.py中添加导出路由app.route(/leads/export) def export_leads(): import csv from io import StringIO from flask import Response output StringIO() writer csv.writer(output) writer.writerow([ID, 姓名, 电话, 邮箱, 来源, 状态, 分配给, 标签, 创建时间]) leads Lead.query.all() for lead in leads: writer.writerow([ lead.id, lead.name, lead.phone, lead.email, lead.source, lead.status, lead.assigned_to, lead.tags, lead.created_at.strftime(%Y-%m-%d %H:%M:%S) ]) output.seek(0) return Response( output.getvalue(), mimetypetext/csv, headers{Content-disposition: attachment; filenamecrm_leads_export.csv} )在index.html中加导出按钮a href{{ url_for(export_leads) }} stylemargin-left:10px; 导出线索CSV/a此功能价值极大销售下载CSV后可用Excel透视表自己分析“哪个来源的线索转化率最高”比等你开发报表更快。这也是让业务方从“被动使用者”变成“主动参与者”的关键一步。6. 我的原型交付 checklist上线前必做的7件事与一条后悔药交付CRM原型不是代码git push就结束。我带过的每个模拟项目X上线演示前夜都会逐项核对这份checklist。它不追求完美但确保“不丢人”——当销售总监问“这条线索为什么没进我的池子”你能30秒内打开crm.db定位到assigned_to字段而不是手忙脚乱查日志。以下是我压箱底的7条按执行顺序排列最后一条是真正的“后悔药”。6.1 数据库备份与初始数据注入原型跑通后立刻备份crm.db为crm_demo.db并注入10条模拟数据含不同状态、来源、标签确保每次git clone后python app.py能直接看到真实场景# 备份 cp crm.db crm_demo.db # 创建 init_data.py运行一次 from app import create_app from models import db, Lead app create_app() with app.app_context(): db.create_all() # 插入10条模拟数据... db.session.commit()为什么重要避免演示时因空库导致“首页一片空白”业务方第一印象直接崩塌。6.2 关闭调试模式与设置密钥app.py中debugTrue必须改为False且SECRET_KEY不能硬编码# app.py app.config[SECRET_KEY] os.environ.get(SECRET_KEY, dev-key-change-in-prod) # 启动时设置环境变量 # export SECRET_KEYa-very-secure-random-string玄学警告曾有项目因SECRET_KEY未设flash()消息在生产环境完全不显示销售以为系统坏了。6.3 验证所有表单提交的错误处理手动在表单中留空必填字段、输入超长字符串、提交特殊字符如script确认页面不崩溃且有友好提示。Flask默认500错误页太丑加一个简单错误页app.errorhandler(404) def not_found(e): return render_template(error.html, error页面未找到), 404 app.errorhandler(500) def server_error(e): return render_template(error.html, error系统内部错误请稍后重试), 5006.4 检查URL路由是否全部可访问用curl -I http://127.0.0.1:5000/xxx测试所有路由/,/leads/new,/leads/1/update_status,/leads/export确认返回200或302。漏掉一个路由演示时点不动按钮就是灾难。6.5 测试移动端适配用Chrome DevTools切到iPhone X尺寸打开首页。表单字段是否可点击按钮是否够大表格是否横向滚动原型阶段不追求响应式设计但至少保证销售用手机能录线索。6.6 文档化“三句话说明”写一个README.md只包含三句话“这是一个CRM客户关系管理系统原型支持线索录入、状态更新、商机创建和基础统计。”“运行方式pip install -r requirements.txt python app.py访问http://127.0.0.1:5000。”“默认管理员无密码所有操作均可直接使用数据库文件为crm.db双击可用DB Browser打开。”——业务方不需要知道Flask是什么他们只需要知道“怎么用”。6.7 最后一条后悔药——一键回滚到初始状态在app.py中加一个隐藏路由仅本地开发用点击即清空所有数据重置为初始10条app.route(/reset-db) def reset_db(): if not app.debug: return 仅开发环境可用, 403 db.drop_all() db.create_all() # 重新注入初始数据... return redirect(url_for(index))演示中途发现数据错乱点一下/reset-db3秒回到干净状态。这招救过我三次——一次是销售误操作把所有线索状态改成lost一次是测试时数据库锁死还有一次是……算了不说了。希望帮到你。本文还有配套的精品资源点击获取

关于恒美微站

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

快速链接

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

服务项目

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

联系方式

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

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