1. 为什么我选择 WorkBuddy Flask SQLite 这套组合1.1 从想做个站到真的跑起来之间差了什么很多人对建站的理解停留在两个极端要么觉得必须学完整套前端后端才能动手要么以为拖拽几下就能上线一个能用的产品。我刚开始也是这么想的直到真正动手才发现从想做个站到每天有人访问之间隔着的不是技术难度而是一整套流程的打通——域名怎么解析、后端怎么跑起来、数据存哪里、内容怎么持续更新、出问题了怎么排查。这些环节单拎出来都不难但串在一起就是另一回事了。我用 WorkBuddy 配合 Flask 和 SQLite 从零搭了一个站并且坚持日更了一段时间。这篇文章就是把整个过程拆开揉碎把我踩过的坑、试过的方案、最终跑通的路径完整记录下来。不管你是刚接触 Python 的新手还是已经写过一些脚本想做个完整项目的开发者这套组合都值得一试。核心关键词就三个WorkBuddy 负责提效Flask 负责后端逻辑SQLite 负责数据存储。三者配合起来一台普通电脑就能跑不需要买服务器不需要配数据库集群本地就能完成开发和调试。1.2 为什么是 Flask 而不是 Django 或 FastAPIFlask 最大的特点是够用就好。它不像 Django 那样自带 ORM、Admin、认证系统一大堆东西也不像 FastAPI 那样强依赖异步和类型注解。Flask 给你的是一个最小核心路由、请求处理、模板渲染剩下的你自己选。对于个人建站来说这种自由度反而更舒服——你不需要为了用一个功能去理解整个框架的设计哲学。我试过用 Django 搭同样的东西光是理解 settings.py 里的配置项就花了不少时间而且很多默认行为需要覆盖才能符合我的需求。FastAPI 虽然性能好但写同步代码时反而要多想一层。Flask 的另一个好处是资料多遇到问题搜索flask 怎么做xxx基本都能找到直接能用的答案。对于日更这种需要快速迭代的场景Flask 的轻量和灵活是实打实的优势。1.3 SQLite 在个人项目里的真实定位SQLite 经常被低估。很多人一听数据库就觉得要用 MySQL 或 PostgreSQL觉得 SQLite 只是玩具。但实际上对于日访问量几千甚至几万的小站SQLite 完全扛得住。它不需要单独启动服务进程数据就是一个文件备份就是复制文件迁移就是拷贝文件。这种简单性在个人项目里是巨大的优势。我用 SQLite 存文章内容、标签、访问记录整个数据库文件不到 10MB查询响应基本在毫秒级。配合 DB Browser for SQLite 这个可视化工具改数据、看表结构、跑查询都很方便。当然SQLite 也有它的边界高并发写入会锁库不适合多用户同时写。但个人日更站点的写入频率很低这个限制基本碰不到。1.4 WorkBuddy 在整个流程里扮演什么角色WorkBuddy 是我用来提效的工具。它不是一个建站平台而是一个工作台可以理解成把日常开发中重复性的操作集中管理起来。比如我每天要做的检查昨天的访问日志、生成当天的文章草稿、更新数据库、重启 Flask 服务。这些操作如果每次都手动敲命令日更很难坚持。WorkBuddy 的自定义指令功能让我把这些步骤串起来一键执行。它的 skill 机制也很实用可以把常用的代码片段、配置模板存进去需要的时候直接调用。我把自己写的 Flask 路由模板、SQLite 建表语句、文章 Markdown 模板都存成了 skill新建功能时直接套用省去了大量重复劳动。WorkBuddy 有国际版和国内版我用的是国际版界面更简洁功能更新也快一些。安装过程不复杂官网有详细的 WorkBuddy 安装教程跟着走就行。2. 环境搭建从零到 Flask 跑起来2.1 Python 安装与环境配置的避坑指南Python 安装本身不难但有几个细节不注意后面会很难受。首先安装时一定要勾选Add Python to PATH否则命令行里敲 python 会提示找不到命令。其次建议安装 3.10 或以上版本Flask 和 SQLite 的很多新特性需要较新的 Python 支持。我一开始用的是 3.8后来发现某些库的版本兼容有问题升级到 3.11 之后顺畅很多。安装完成后在命令行里执行python --version确认版本。然后建议创建一个虚拟环境命令是python -m venv venv激活命令在 Windows 上是venv\Scripts\activate在 macOS 和 Linux 上是source venv/bin/activate。虚拟环境的好处是项目依赖隔离不会和系统里的其他 Python 包冲突。我试过不创建虚拟环境直接装 Flask结果后来做另一个项目时版本冲突排查了很久。VSCode 的 Python 环境配置也值得说一下。安装 Python 扩展后按 CtrlShiftP 打开命令面板输入Python: Select Interpreter选择刚才创建的虚拟环境里的 python。这样 VSCode 的终端和调试器都会用这个环境不会出现明明装了库却提示找不到的情况。2.2 Flask 安装与最小可运行应用装 Flask 就一行命令pip install flask。装完之后新建一个app.py写一个最小应用from flask import Flask app Flask(__name__) app.route(/) def index(): return Hello, WorkBuddy! if __name__ __main__: app.run(debugTrue)运行python app.py浏览器打开http://127.0.0.1:5000就能看到结果。debugTrue的作用是代码修改后自动重启开发阶段非常方便。但注意上线时一定要关掉 debug 模式否则会暴露代码细节有安全风险。Flask 的路由系统是它的核心。app.route(/)定义了一个 URL 规则函数返回值就是响应内容。你可以定义多个路由比如/about、/article/int:idFlask 会自动把 URL 里的参数传给函数。这种设计让 URL 结构很清晰也方便后续扩展。2.3 SQLite 的安装与可视化工具选择SQLite 本身不需要安装Python 标准库就带了sqlite3模块。但为了管理数据方便建议装一个可视化工具。我用的是 DB Browser for SQLite免费开源支持 Windows、macOS、Linux。下载安装后直接打开.db文件就能看到表结构、数据、索引还能直接跑 SQL 查询。另一个选择是 VSCode 的 SQLite 扩展装完之后在编辑器里就能查看数据库文件不用切换窗口。我两个都用DB Browser 适合做复杂的表结构修改和查询调试VSCode 扩展适合快速查看数据。如果你用 Android Studio 开发移动端它自带的 Database Inspector 也能看 SQLite 数据但那是针对 Android 应用的和 Flask 项目关系不大。建库的 SQL 语句很简单CREATE TABLE articles ( id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT NOT NULL, content TEXT NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP );在 DB Browser 里点Execute SQL粘贴进去执行就行。或者在 Python 代码里用sqlite3模块执行也可以。我习惯在代码里建表这样部署到新环境时不用手动操作。2.4 WorkBuddy 安装与初始配置WorkBuddy 的安装过程官网写得很清楚下载对应系统的安装包一路下一步就行。安装完成后第一次打开会让你选择工作目录建议选一个专门放项目的文件夹不要放在桌面或下载目录里避免文件混乱。初始配置里最重要的是自定义指令的设置。我建了几个常用指令start-flask用来启动 Flask 服务backup-db用来备份 SQLite 数据库new-article用来生成新文章的模板文件。这些指令配置一次后面每天用的时候直接点一下就行省去了敲命令的麻烦。WorkBuddy 的 skill 功能我主要用来存代码模板。比如 Flask 的路由模板、SQLite 的增删改查语句、HTML 的基础结构都存成 skill新建文件时直接插入。这个功能看起来简单但日更的时候能省不少时间。WorkBuddy 和 CodeBuddy 的区别在于WorkBuddy 更偏向工作流管理CodeBuddy 更偏向代码生成和补全两者定位不同可以配合使用。3. 核心功能实现从路由到数据库的完整链路3.1 Flask 路由设计与模板渲染路由是 Flask 应用的骨架。我的站点主要有这几个页面首页文章列表、文章详情页、标签筛选页、关于页。对应的路由设计如下app.route(/) def index(): # 查询文章列表按时间倒序 articles get_all_articles() return render_template(index.html, articlesarticles) app.route(/article/int:article_id) def article_detail(article_id): article get_article_by_id(article_id) if article is None: abort(404) return render_template(article.html, articlearticle) app.route(/tag/tag_name) def tag_filter(tag_name): articles get_articles_by_tag(tag_name) return render_template(tag.html, articlesarticles, tagtag_name)render_template会去templates文件夹里找对应的 HTML 文件。Flask 默认使用 Jinja2 模板引擎支持变量替换、循环、条件判断。比如在index.html里可以这样写{% for article in articles %} div classarticle-card h2a href/article/{{ article.id }}{{ article.title }}/a/h2 p{{ article.summary }}/p span{{ article.created_at }}/span /div {% endfor %}Jinja2 的语法很直观{{ }}里放变量{% %}里放逻辑。模板继承也很实用可以定义一个base.html放公共的头部和尾部其他页面继承它只写差异部分。这样改导航栏的时候只需要改一个文件。3.2 SQLite 数据库操作增删改查的实战写法Python 操作 SQLite 的核心就是sqlite3模块。连接数据库、执行 SQL、提交事务、关闭连接四步走。我封装了几个函数让调用更简洁import sqlite3 DB_PATH blog.db def get_db(): conn sqlite3.connect(DB_PATH) conn.row_factory sqlite3.Row # 让查询结果可以按列名访问 return conn def get_all_articles(): conn get_db() cursor conn.execute(SELECT * FROM articles ORDER BY created_at DESC) articles cursor.fetchall() conn.close() return articles def insert_article(title, content, summary, tags): conn get_db() conn.execute( INSERT INTO articles (title, content, summary, tags) VALUES (?, ?, ?, ?), (title, content, summary, tags) ) conn.commit() conn.close()conn.row_factory sqlite3.Row这行很关键它让查询结果可以通过列名访问比如article[title]而不是只能用索引article[0]。这个细节在模板渲染时特别有用代码可读性高很多。SQLite 的UPDATE语句写法def update_article(article_id, title, content): conn get_db() conn.execute( UPDATE articles SET title ?, content ? WHERE id ?, (title, content, article_id) ) conn.commit() conn.close()注意WHERE条件一定要写否则会更新整张表。我刚开始学的时候犯过这个错误把一篇文章的标题改成了所有文章的标题幸好有备份。所以每次执行UPDATE或DELETE之前先用SELECT确认条件对不对这个习惯能救命。3.3 关键词相似度匹配的轻量实现我的站点有一个智能匹配功能用来关联相关的文章。实现方式是基于关键词的相似度计算。最简单的做法是 Jaccard 相似度把两篇文章的关键词集合取交集除以并集得到 0 到 1 之间的相似度值。def jaccard_similarity(tags1, tags2): set1 set(tags1.split(,)) set2 set(tags2.split(,)) intersection set1 set2 union set1 | set2 if not union: return 0 return len(intersection) / len(union)这个算法简单但有效。比如一篇文章的标签是Flask,Python,建站另一篇是Flask,SQLite,数据库交集是Flask并集是四个标签相似度就是 0.25。相似度超过某个阈值比如 0.2就推荐给读者。如果要更精准可以用 TF-IDF 或词向量但那需要额外的库和计算资源。对于个人站点Jaccard 已经够用了。我实测下来推荐的相关文章质量还不错读者点击率比随机推荐高不少。3.4 WorkBuddy 自定义指令串联日常操作日更最怕的是流程繁琐。我把每天的操作拆成了几个 WorkBuddy 指令daily-check检查数据库连接、文章数量、最近一篇的发布时间new-draft在drafts文件夹里生成一个带日期和模板的 Markdown 文件publish把草稿内容写入数据库更新首页backup复制blog.db到backups文件夹文件名带日期这些指令在 WorkBuddy 里配置一次后面每天点一下就行。publish指令里我加了一个确认步骤会先显示文章标题和摘要确认无误后再写入数据库。这个设计避免了我误操作发布未完成的草稿。WorkBuddy 的 skill 功能在这里也派上了用场。我把文章模板存成 skill新建草稿时自动插入标题、日期、标签、正文占位符。这样每天只需要填内容不用重复写格式。4. 日更实操从草稿到发布的完整流程4.1 每日内容生产的时间分配日更最难的不是技术是坚持。我给自己定了一个节奏早上花 30 分钟写草稿中午花 10 分钟修改和配图晚上花 5 分钟发布和备份。这个时间分配的关键是写和发分开写的时候不想发布的事发的时候不纠结内容质量。草稿我直接用 Markdown 写存在drafts文件夹里。文件名格式是2025-01-15-文章标题.md这样按文件名排序就是按日期排序。写完之后用 WorkBuddy 的publish指令读取文件内容解析标题和正文写入 SQLite 数据库。解析 Markdown 的代码很简单import re def parse_markdown(filepath): with open(filepath, r, encodingutf-8) as f: content f.read() # 提取第一个一级标题作为文章标题 title_match re.search(r^# (.)$, content, re.MULTILINE) title title_match.group(1) if title_match else 无标题 # 去掉标题行剩下的作为正文 body re.sub(r^# .$, , content, count1, flagsre.MULTILINE).strip() return title, body这个解析逻辑不复杂但要注意编码问题。Windows 上默认可能是 GBK所以打开文件时一定要指定encodingutf-8否则中文会乱码。我踩过这个坑文章发出来全是问号排查了半天才发现是编码问题。4.2 发布流程的自动化与人工确认发布流程我设计成自动手动确认的模式。WorkBuddy 的publish指令会自动完成以下步骤读取草稿文件、解析标题和正文、生成摘要、提取标签、写入数据库、刷新首页缓存。但在写入数据库之前会弹出一个确认框显示解析结果我确认无误后才继续。这个设计的原因是自动解析偶尔会出错比如标题里有特殊字符、正文里有代码块包含#号。人工确认一下避免发出去才发现问题。确认之后写入数据库的操作是原子的要么全部成功要么全部回滚不会出现半截数据。发布完成后WorkBuddy 会自动执行备份指令把blog.db复制到backups文件夹文件名格式是blog-2025-01-15.db。这样即使后面操作失误也能恢复到之前的状态。备份文件我保留最近 30 天的更早的自动删除避免占太多磁盘空间。4.3 数据库更新与首页刷新文章写入数据库后首页需要刷新才能显示新内容。Flask 默认每次请求都会重新查询数据库所以不需要额外操作。但如果你用了缓存就需要手动清除。我没用缓存因为 SQLite 的查询速度足够快首页加载时间在 100ms 以内没必要加缓存层。不过有一个细节要注意Flask 的debugTrue模式下代码修改会自动重启但数据库文件的变化不会触发重启。所以如果你在 DB Browser 里手动改了数据Flask 不会自动感知需要刷新页面才能看到。这个不是问题只是要知道。首页的文章列表我做了分页每页显示 10 篇。分页的实现app.route(/page/int:page) def index_page(page): per_page 10 offset (page - 1) * per_page conn get_db() articles conn.execute( SELECT * FROM articles ORDER BY created_at DESC LIMIT ? OFFSET ?, (per_page, offset) ).fetchall() total conn.execute(SELECT COUNT(*) FROM articles).fetchone()[0] total_pages (total per_page - 1) // per_page conn.close() return render_template(index.html, articlesarticles, current_pagepage, total_pagestotal_pages)LIMIT和OFFSET是 SQLite 分页的标准写法。total_pages的计算用了向上取整确保最后一页不满 10 篇时也能正确显示页码。4.4 日更坚持下来的几个关键习惯日更能不能坚持技术只占三成习惯占七成。我总结了几个对我帮助最大的习惯第一固定时间写。我每天早上 8 点到 8 点半写草稿雷打不动。时间固定了大脑会自动进入状态不用每次纠结什么时候写。第二降低单篇难度。不要想着每篇都写深度长文有时候一篇 500 字的实操小技巧反而更受欢迎。日更的核心是持续不是完美。第三提前储备选题。我建了一个topics.md文件平时想到什么选题就记下来写的时候直接从里面挑不用临时想。第四发布后立即备份。这个习惯让我避免了好几次数据丢失。有一次我误删了一篇文章直接从备份恢复前后不到一分钟。第五用 WorkBuddy 减少重复操作。把能自动化的都自动化把精力留给内容本身。这是工具存在的意义。5. 常见问题与排查技巧实录5.1 Flask 启动报错的几种典型情况Flask 启动报错最常见的是端口被占用。错误信息是OSError: [Errno 98] Address already in use意思是 5000 端口已经被别的程序用了。解决办法有两个一是找到占用端口的程序并关掉二是换一个端口启动比如app.run(port5001)。我一般直接换端口省事。第二种常见错误是模块找不到提示ModuleNotFoundError: No module named flask。这通常是因为虚拟环境没激活或者 VSCode 选的解释器不对。检查方法是命令行里执行which pythonmacOS/Linux或where pythonWindows确认指向的是虚拟环境里的 python。第三种是模板找不到提示TemplateNotFound: index.html。这是因为templates文件夹的位置不对。Flask 默认在应用根目录下找templates文件夹如果你的app.py在子文件夹里就需要指定template_folder参数。5.2 SQLite 数据库锁定的处理方式SQLite 的锁问题在写入频繁时会出现错误信息是database is locked。原因是 SQLite 默认的锁机制是写操作会锁整张库如果同时有另一个写操作在等待就会超时。对于个人站点这个问题很少遇到但如果你用了多线程或者多个进程同时写就可能碰到。解决办法有几个一是设置timeout参数让连接等待锁释放而不是立即报错conn sqlite3.connect(DB_PATH, timeout10)二是把写操作集中到一个地方避免并发写。我的做法是所有写操作都通过 WorkBuddy 的指令执行同一时间只有一个写操作在跑从根本上避免了锁冲突。三是考虑用 WAL 模式它允许读写并发conn.execute(PRAGMA journal_modeWAL)WAL 模式下读操作不会被写操作阻塞写操作也不会被读操作阻塞。对于读多写少的站点这个模式很合适。但要注意WAL 模式会生成额外的-wal和-shm文件备份的时候要一起复制。5.3 中文乱码的排查与解决中文乱码是 Python 建站的高频问题。表现是页面上显示问号或者乱码字符。原因通常是编码不一致文件保存的编码、Python 读取的编码、HTML 声明的编码、数据库存储的编码四个环节任何一个不一致都会出问题。排查步骤首先确认 HTML 模板里有meta charsetUTF-8。然后确认 Python 打开文件时指定了encodingutf-8。接着确认 SQLite 数据库的编码是 UTF-8默认就是。最后确认 Flask 返回响应时设置了正确的 Content-Type。Flask 默认的响应编码就是 UTF-8一般不需要额外设置。但如果用了jsonify返回中文可能需要设置app.config[JSON_AS_ASCII] False否则中文会被转义成\uXXXX格式。这个在调试 API 时经常遇到。5.4 常见问题速查表问题现象可能原因解决方法端口被占用5000 端口已被其他程序使用换端口app.run(port5001)模块找不到虚拟环境未激活或解释器选错激活虚拟环境VSCode 重选解释器模板找不到templates 文件夹位置不对确认文件夹在应用根目录或指定 template_folder数据库锁定并发写入冲突设置 timeout或改用 WAL 模式中文乱码编码不一致统一使用 UTF-8检查文件、HTML、数据库编码静态文件 404static 文件夹位置不对确认 static 文件夹在应用根目录修改代码不生效debug 模式未开启设置app.run(debugTrue)数据库文件找不到路径不对使用绝对路径或确认工作目录5.5 几个让我少走弯路的实操心得第一个心得每次改数据库之前先备份。这个习惯看起来麻烦但关键时刻能救命。我有一次执行DELETE语句忘了加WHERE整张表被清空幸好有备份五分钟就恢复了。从那以后我养成了改之前先复制一份的习惯。第二个心得Flask 的url_for函数比硬编码 URL 好。比如url_for(index)会自动生成/url_for(article_detail, article_id1)会生成/article/1。这样即使你改了路由规则模板里的链接也会自动更新不用一个个改。第三个心得SQLite 的AUTOINCREMENT不是必须的。INTEGER PRIMARY KEY本身就会自增加AUTOINCREMENT反而会多维护一个序列表略微影响性能。除非你需要 ID 永不重复即使删除后也不复用否则不加也行。第四个心得WorkBuddy 的指令可以嵌套调用。比如publish指令里可以调用backup指令这样发布完自动备份不用手动执行两次。这个功能在配置文档里没写是我试出来的。第五个心得日志比调试器好用。Flask 的app.logger可以记录运行时的信息出问题时看日志比一步步调试快。我在关键操作处都加了日志比如文章发布成功、数据库连接失败、模板渲染异常。这些日志在排查问题时非常有用。6. 部署与持续运行的几个实际考量6.1 本地运行与对外访问的差异本地开发时用app.run(debugTrue)就够了但要让别人访问就需要考虑更多。首先是 host 设置默认是127.0.0.1只能本机访问。改成0.0.0.0可以让同一局域网内的其他设备访问app.run(host0.0.0.0, port5000, debugFalse)但注意debugFalse是必须的否则会有安全风险。另外Flask 自带的开发服务器性能有限不适合正式对外服务。如果访问量稍大建议用 Waitress 或 Gunicorn 这样的 WSGI 服务器。Waitress 在 Windows 上表现很好安装pip install waitress启动命令from waitress import serve serve(app, host0.0.0.0, port8080)这样就用 Waitress 替代了 Flask 的开发服务器稳定性和并发能力都好很多。6.2 数据备份与恢复的标准操作SQLite 的备份很简单复制.db文件就行。但要注意如果数据库正在写入直接复制可能得到不完整的数据。安全的做法是先执行VACUUM INTO命令VACUUM INTO backup.db;这个命令会创建一个干净的备份文件不受当前写入操作影响。恢复的时候把备份文件复制回原位置重启 Flask 就行。我设置了一个 WorkBuddy 指令每天发布完成后自动执行备份备份文件按日期命名保留最近 30 天。这个习惯让我在任何时候都有退路。6.3 性能优化的几个实用手段SQLite 的性能优化主要靠索引。在经常查询的字段上建索引比如created_at和tagsCREATE INDEX idx_created_at ON articles(created_at); CREATE INDEX idx_tags ON articles(tags);索引会让查询变快但会稍微降低写入速度。对于读多写少的站点这个取舍是值得的。Flask 层面的优化主要是减少数据库查询次数。比如首页需要文章列表和标签列表如果分两次查询就多了一次数据库连接。可以合并成一次查询或者用缓存。我用的是最简单的办法把标签列表缓存在内存里每 10 分钟更新一次。这样大部分请求都不用查数据库。另一个优化是开启 SQLite 的查询缓存conn.execute(PRAGMA cache_size -2000) # 缓存 2MB 数据这个设置让 SQLite 在内存里缓存更多数据减少磁盘读取。对于小站点效果很明显。6.4 后续可以扩展的方向这套架构虽然简单但扩展性不差。后续想加功能有几个方向一是加评论功能。新建一张comments表关联article_id前端加一个评论框后端加一个提交路由。工作量不大但能提升互动性。二是加搜索功能。SQLite 支持LIKE查询简单的搜索用SELECT * FROM articles WHERE title LIKE %关键词%就行。如果要更精准可以用 FTS5 全文搜索扩展SQLite 自带不需要额外安装。三是加 RSS 订阅。生成一个feed.xml文件按 RSS 2.0 格式输出文章列表。这个对技术读者很友好很多人习惯用 RSS 阅读器追更。四是加访问统计。在文章详情页加一个计数器每次访问UPDATE articles SET views views 1 WHERE id ?。简单但实用能知道哪些文章受欢迎。这些扩展都不需要改架构在现有基础上加就行。Flask 和 SQLite 的轻量特性在这里体现得很明显想加什么就加什么不用动底层。6.5 关于工具选择的个人体会WorkBuddy、Flask、SQLite 这套组合我用下来的感受是刚刚好。WorkBuddy 解决了流程自动化的问题Flask 解决了 Web 框架的问题SQLite 解决了数据存储的问题。三者各司其职没有过度设计也没有功能缺失。我试过用更重的方案Django PostgreSQL Celery功能确实强大但配置复杂启动慢对于个人日更站点来说完全是杀鸡用牛刀。也试过更轻的方案纯静态 HTML JSON 文件简单是简单但每次加文章都要手动改 HTML日更根本坚持不下来。Flask SQLite 的平衡点找得很好比静态站点灵活比全栈框架轻量。配合 WorkBuddy 的自动化日更的流程被压缩到了极致。如果你也想做一个能持续更新的个人站点这套组合值得试试。不用一开始就追求完美先跑起来再慢慢优化。日更的核心是持续技术只是手段。
