1. 为什么我选择 WorkBuddy Flask SQLite 这套组合先说结论这套组合不是拍脑袋选的是我在试过 WordPress、Shopify 和纯静态源码建站之后针对“个人内容站 日更 数据自己攥在手里”这个具体需求筛出来的。WorkBuddy 负责把建站和日常运维的重复劳动接过去Flask 负责把页面逻辑和数据接口捏在自己手里SQLite 负责用零运维成本扛住个人站点的读写量。三者拼在一起最大的好处是我不需要为了日更去学一整套前端工程化也不需要为了一个数据库去租一台常年跑着的服务器。很多人一上来就问“自建站和 Shopify、WordPress 到底差在哪”。我自己的判断标准很简单你要的是“开店卖货”还是“沉淀内容 自己掌控数据”。Shopify 是典型的电商托管商品、订单、支付都帮你封装好了代价是每月固定成本和对平台的依赖WordPress 是内容托管的老牌选手插件生态庞大但插件一多安全和性能就开始互相拖后腿日更时后台加载慢、插件冲突是家常便饭。自建站的核心价值在于代码、数据库文件、部署方式全在你手里你想加一个“农产品价格数据可视化”这种定制页面不用等插件作者更新自己写个路由就完事。WorkBuddy 在这套流程里扮演的角色更像一个“懂建站流程的协作搭子”。它不是一个单纯的代码补全工具而是能围绕“建站”这个目标帮你把项目结构、路由设计、数据库表结构、甚至部署脚本一步步推出来。我实测下来它对 Flask 这种轻量框架的理解比较到位生成的代码不会一上来就给你堆一堆用不上的依赖。这一点很关键因为新手建站最容易死在“依赖装了一堆跑不起来还不知道错在哪”。提示选工具之前先想清楚你的站点是“内容型”还是“交易型”。内容型优先自建 Flask交易型优先成熟托管平台别为了技术情怀硬上自建最后维护成本会教你做人。SQLite 的选择也是同样的逻辑。个人日更站点日访问量大概率在几百到几千这个量级SQLite 的单文件读写完全扛得住。它的好处是数据库就是一个.db文件备份就是复制文件迁移就是拷走文件用 DB Browser for SQLite 这种可视化工具打开就能看数据对新手极其友好。你不需要装 MySQL、不需要配用户权限、不需要管连接池。等到哪天数据量真的上来了再迁移到 PostgreSQL 也不迟Flask 配合 SQLAlchemy 的话迁移成本可控。2. 环境搭建Python、Flask、SQLite 三件套怎么装才不踩坑2.1 Python 安装与虚拟环境隔离Python 安装这一步新手最容易犯的错是“直接装系统全局环境然后所有项目共用一套依赖”。我踩过这个坑一个项目升级了 Flask 版本另一个老项目直接跑不起来。正确做法是每个项目一个虚拟环境。Windows 下去官网下载安装包安装时务必勾选“Add Python to PATH”这一步不勾后面命令行里敲python会提示找不到命令。macOS 和 Ubuntu 用户相对省心系统自带或者用包管理器装都行Ubuntu 上sudo apt install python3 python3-pip python3-venv基本够用。装完之后验证python --version pip --version然后进项目目录建虚拟环境python -m venv venvWindows 激活venv\Scripts\activatemacOS / Linux 激活source venv/bin/activate激活后命令行前面会出现(venv)前缀这时候装的包才只属于这个项目。我个人的习惯是虚拟环境目录永远叫venv并且写进.gitignore避免把几百兆的依赖提交到代码仓库。2.2 Flask 安装与最小可运行站点虚拟环境激活状态下pip install flask这里有个细节不要一上来就pip install一大堆东西。先把 Flask 装上跑通一个最小页面再按需加依赖。我见过太多人一开始就装 Flask-SQLAlchemy、Flask-Login、Flask-Migrate结果第一个页面都跑不起来排查半天发现是某个扩展和 Flask 版本不兼容。最小站点app.pyfrom flask import Flask app Flask(__name__) app.route(/) def index(): return h1站点跑起来了/h1 if __name__ __main__: app.run(debugTrue)debugTrue只在开发阶段用它会自动重载代码并显示详细错误页方便调试。上线必须关掉否则错误页会暴露源码路径等敏感信息。2.3 SQLite 与可视化工具SQLite 本身不需要单独安装服务Python 标准库自带sqlite3模块。但你需要一个可视化工具来看数据我推荐 DB Browser for SQLite免费、跨平台、打开.db文件就能看表结构和数据改数据也方便。Android Studio 里也有 SQLite 的可视化工具但那是给移动端开发用的做 Web 站没必要绕那一圈。建库最简单的方式是写个初始化脚本import sqlite3 conn sqlite3.connect(site.db) cursor conn.cursor() cursor.execute( CREATE TABLE IF NOT EXISTS articles ( id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT NOT NULL, content TEXT NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ) conn.commit() conn.close()跑一次这个脚本site.db就生成了。之后用 DB Browser 打开能看到articles表。这里我特意用IF NOT EXISTS避免重复执行时报错这个习惯在写初始化脚本时很实用。注意SQLite 数据库文件默认没有加密。如果你的站点涉及敏感数据要么别用 SQLite要么在应用层做字段加密。个人内容站一般不需要但心里要有数。3. 用 WorkBuddy 把建站流程拆成可执行步骤3.1 项目结构设计别把所有代码塞进一个文件新手最容易写成“一个app.py走天下”几百行之后自己都找不到路由在哪。我建议一开始就分目录myblog/ ├── app.py ├── models.py ├── routes.py ├── templates/ │ ├── index.html │ └── article.html ├── static/ │ ├── style.css │ └── app.js └── site.dbapp.py只负责创建应用和注册蓝图models.py管数据库操作routes.py管路由模板放templates静态资源放static。这个结构不复杂但能让项目在日更过程中不至于失控。我用 WorkBuddy 的时候会直接把“帮我按 Flask 蓝图方式拆分项目结构包含文章列表、文章详情、后台发布三个路由”这样的指令丢给它它给出的骨架基本可用我再按自己的习惯微调。WorkBuddy 的自定义指令功能在这里很省事你可以把“Flask 项目规范”存成一条常用指令以后每次新建项目直接调用。3.2 数据库表结构日更站点需要哪些字段日更站点核心就一张文章表但字段设计要考虑后续扩展。我的表结构字段名类型说明idINTEGER主键自增titleTEXT文章标题非空slugTEXTURL 友好标识唯一contentTEXT正文存 Markdown 或 HTMLsummaryTEXT摘要列表页展示statusINTEGER0 草稿1 已发布created_atTIMESTAMP创建时间updated_atTIMESTAMP更新时间slug这个字段很多人会忽略但它对 SEO 和 URL 可读性很重要。比如/article/flask-sqlite-guide比/article/123友好得多。status字段让你可以先存草稿再发布日更场景下很实用。3.3 路由与模板把数据从数据库搬到页面Flask 的路由写法很直观app.route(/article/slug) def article_detail(slug): conn sqlite3.connect(site.db) conn.row_factory sqlite3.Row cursor conn.cursor() cursor.execute(SELECT * FROM articles WHERE slug ? AND status 1, (slug,)) row cursor.fetchone() conn.close() if row is None: return 文章不存在, 404 return render_template(article.html, articlerow)这里有两个细节值得说。第一conn.row_factory sqlite3.Row让查询结果可以像字典一样用列名访问模板里写{{ article.title }}就行不用记索引。第二SQL 里用?占位符传参不要用字符串拼接这是防 SQL 注入的基本功。模板article.html!DOCTYPE html html head title{{ article.title }}/title /head body h1{{ article.title }}/h1 p{{ article.created_at }}/p div{{ article.content | safe }}/div /body /html| safe过滤器表示内容按 HTML 渲染因为我的正文是自己写的可信。如果正文来自用户提交绝对不能加safe否则就是 XSS 漏洞。4. 日更实操从写稿到发布的完整流水线4.1 内容生产Markdown 写稿 脚本入库日更最大的敌人不是技术是流程摩擦。如果每发一篇文章都要打开后台、填表单、点保存坚持不了几天就会放弃。我的做法是本地用 Markdown 写稿文件名就是 slug然后写个脚本批量入库。import sqlite3 import os import re def parse_markdown(filepath): with open(filepath, r, encodingutf-8) as f: text f.read() # 简单约定第一行是标题其余是正文 lines text.split(\n) title lines[0].strip(# ).strip() content \n.join(lines[1:]) return title, content def publish(filepath): slug os.path.splitext(os.path.basename(filepath))[0] title, content parse_markdown(filepath) conn sqlite3.connect(site.db) cursor conn.cursor() cursor.execute( INSERT INTO articles (title, slug, content, status) VALUES (?, ?, ?, 1) ON CONFLICT(slug) DO UPDATE SET title excluded.title, content excluded.content, updated_at CURRENT_TIMESTAMP , (title, slug, content)) conn.commit() conn.close() print(f已发布{title})这个脚本的关键是ON CONFLICT(slug) DO UPDATE也就是 SQLite 的 upsert 语法。同一篇文章改完再跑一次它会更新而不是报错。日更场景下我经常是先发一版第二天再补充内容这个语法省了很多手动操作。4.2 发布节奏定时任务 草稿箱日更不等于每天手动点发布。我的做法是周末集中写 5 到 7 篇全部以status 0存草稿然后写个定时脚本每天把一篇草稿改成已发布。import sqlite3 from datetime import datetime def publish_one(): conn sqlite3.connect(site.db) cursor conn.cursor() cursor.execute( UPDATE articles SET status 1, created_at ? WHERE id ( SELECT id FROM articles WHERE status 0 ORDER BY id LIMIT 1 ) , (datetime.now(),)) conn.commit() conn.close()这个UPDATE配合子查询每次把最早的一篇草稿发布出去。Linux 下用crontab每天定时跑一次Windows 下用任务计划程序。这样即使某天忙得没时间写站点也不会断更。提示定时发布脚本要加日志记录每次发布了哪篇。不然哪天发现文章顺序乱了你都不知道是哪一步出的问题。4.3 数据备份SQLite 的备份就是复制文件SQLite 最大的运维优势在这里体现得淋漓尽致。备份脚本cp site.db backups/site_$(date %Y%m%d).db一行搞定。我一般保留最近 30 天的备份更早的删掉。恢复的时候把对应日期的文件复制回site.db就行。这个操作简单到不需要任何数据库知识对个人站长来说非常友好。5. 常见问题与排查技巧实录5.1 数据库锁日更时最容易遇到的坑SQLite 是文件级锁同一时间只允许一个写操作。如果你的站点访问量稍微上来一点或者定时脚本和用户请求撞在一起就会报database is locked。我踩过这个坑排查了半天才发现是定时发布脚本和页面请求同时写库。解决办法有三个层次。第一写操作尽量短不要在事务里做耗时计算。第二连接时设置超时conn sqlite3.connect(site.db, timeout10)这样遇到锁会等 10 秒再报错而不是立刻失败。第三如果写入确实频繁考虑用 WAL 模式conn.execute(PRAGMA journal_modeWAL)WAL 模式下读写可以并发对个人站点来说基本够用。5.2 中文乱码编码问题排查Flask 默认用 UTF-8但如果你在 Windows 下用某些编辑器写模板文件可能存成 GBK页面就乱码。排查方法用浏览器开发者工具看响应头的Content-Type确认是text/html; charsetutf-8。如果模板文件编码不对用 VS Code 右下角的编码切换成 UTF-8 重新保存。5.3 静态文件 404路径问题Flask 的静态文件默认在static目录模板里引用要写{{ url_for(static, filenamestyle.css) }}不要写死/static/style.css。写死的话一旦部署到子路径下就会 404。这个坑我在部署到 Ubuntu 服务器时踩过本地好好的上线就样式全丢。5.4 常见问题速查表现象可能原因解决方向database is locked并发写冲突加 timeout、开 WAL、缩短事务页面中文乱码文件编码非 UTF-8统一存为 UTF-8静态文件 404路径写死用 url_for 生成模板变量不显示变量名拼写错误检查 render_template 传参端口被占用上次进程没退干净换端口或杀进程修改代码不生效debug 未开或缓存开 debug 或重启服务6. 部署上线从本地到服务器的最后一公里本地跑通只是第一步部署才是真正见真章的地方。Flask 自带的开发服务器不能用于生产性能差且不安全。生产环境我推荐 Gunicorn Nginx 的组合。Ubuntu 服务器上pip install gunicorn gunicorn -w 4 -b 127.0.0.1:8000 app:app-w 4表示 4 个 worker 进程一般按 CPU 核心数设置。然后 Nginx 做反向代理把 80 端口的请求转发到 8000。Nginx 配置核心片段server { listen 80; server_name yourdomain.com; location / { proxy_pass http://127.0.0.1:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } location /static/ { alias /path/to/myblog/static/; } }静态文件交给 Nginx 直接返回不走 Flask这样能省不少性能。这个配置我用了很久日更站点完全扛得住。WorkBuddy 在部署环节也能帮上忙比如让它生成 systemd 服务文件保证 Gunicorn 开机自启、崩溃自动重启。这类模板化配置交给它生成比我自己手写快得多也少出错。7. 我在这套流程里攒下的几条实操心得第一条别追求一步到位。我见过太多人建站第一天就想把评论、搜索、标签、RSS 全做完结果一周后项目烂尾。正确节奏是先跑通“写文章 - 存数据库 - 页面展示”这条最小闭环日更起来之后再逐步加功能。功能是长出来的不是一次性设计出来的。第二条数据库字段宁多勿少。created_at、updated_at、status这些字段建表时顺手加上后面想加就得改表结构、迁移数据麻烦得多。SQLite 加字段用ALTER TABLE就行但生产环境改表永远有风险。第三条把重复操作脚本化。日更站点的核心竞争力是“持续”而持续的天敌是摩擦。凡是每天要做两次以上的操作都值得写个脚本。我现在的发布流程就是写 Markdown、跑一个命令、完事。摩擦降到最低才可能坚持下去。第四条备份要自动化。手动备份一定会忘。我现在的做法是服务器上挂一个每日定时任务把site.db复制到备份目录并按日期命名同时同步一份到本地。SQLite 文件小同步成本几乎为零但关键时刻能救命。第五条WorkBuddy 这类工具是加速器不是替代品。它能帮你生成代码骨架、排查报错、写部署脚本但站点长什么样、内容怎么组织、数据怎么设计这些判断还得你自己做。工具越强越考验使用者的判断力因为生成的东西对不对你得有能力看出来。这套流程我从零跑到现在站点稳定日更数据库文件不到 10MB服务器成本每月几十块。回头看最难的不是技术是决定“用最简单的方式先跑起来”。Flask 够简单SQLite 够简单WorkBuddy 帮你把简单的东西快速拼起来剩下的就是持续写、持续发。
