1. 为什么我选择 WorkBuddy + Flask + SQLite 这套组合
1.1 从零建站的真实需求拆解
很多人一提到建站,第一反应就是 WordPress。我一开始也是这么想的,毕竟生态成熟、插件多、教程满地都是。但真正动手之后才发现,WordPress 的体量对于我这种只想快速搭一个内容站、每天更新几篇文章、偶尔做点数据展示的需求来说,太重了。光是主题定制、插件冲突排查、数据库维护这几件事,就够我喝一壶的。
后来我把目光转向了 Flask。Flask 是一个 Python 的轻量级 Web 框架,核心非常精简,没有强制的项目结构,也没有自带 ORM 和表单验证,你想用什么就装什么。这种“裸奔感”对新手可能不太友好,但对于我这种想完全掌控每一个环节的人来说,反而更舒服。数据库方面,SQLite 是天然的选择——单文件、零配置、不需要单独启动服务,对于日更型的内容站来说,读写压力完全在它的舒适区。
那 WorkBuddy 在这里扮演什么角色?简单说,它是我用来管理日常更新流程的“工作台”。WorkBuddy 本身是一个面向开发者和内容创作者的效率工具,支持自定义指令和技能扩展,可以把我每天重复的操作——比如新建文章草稿、生成摘要、推送到数据库、触发页面刷新——串成一条流水线。这样我不用每次更新都手动去敲 SQL 或者改文件,省下来的时间可以真正花在内容本身上。
提示:如果你之前用过 CodeBuddy,可以把 WorkBuddy 理解成更偏向“工作流编排”的那一类工具。两者定位不同,CodeBuddy 更侧重代码辅助,WorkBuddy 更侧重任务串联和自动化。
1.2 技术选型的对比与取舍
为了让你更清楚我为什么这么选,我把几种常见方案拉出来对比一下:
| 方案 | 上手难度 | 维护成本 | 灵活度 | 适合场景 |
|---|---|---|---|---|
| WordPress | 低 | 中高 | 中 | 博客、企业站、电商 |
| Shopify | 低 | 低 | 低 | 纯电商 |
| Flask + SQLite | 中 | 低 | 极高 | 内容站、数据展示、个人项目 |
| Django | 中高 | 中 | 高 | 大型应用、多用户系统 |
WordPress 和 Shopify 属于“开箱即用”型,但你得接受它们的规则。Flask + SQLite 属于“自己搭积木”型,前期要多花点时间,但后面想怎么改就怎么改。我选后者的核心原因是:我的内容结构比较特殊,不是标准的文章-分类-标签模型,而是带有价格数据、时间序列、可视化图表的混合内容。用 WordPress 做这种站,要么写大量自定义代码,要么装一堆插件然后忍受性能下降。
Flask 的另一个好处是,它和 Python 生态无缝衔接。我可以用 pandas 做数据处理,用 matplotlib 或 plotly 生成图表,用 requests 抓取外部数据,所有这些都在同一个语言体系里完成。SQLite 虽然简单,但配合 SQLAlchemy 或者直接写 SQL,足够支撑日更级别的读写。
1.3 WorkBuddy 在流程中的定位
WorkBuddy 不是建站工具,它不帮你生成页面,也不帮你部署服务器。它的价值在于“把重复动作标准化”。我每天的工作流大概是这样的:
- 打开 WorkBuddy 工作台,触发“新建草稿”指令
- 输入标题和正文,WorkBuddy 自动生成摘要和 slug
- 确认后,WorkBuddy 调用我预设的 Python 脚本,把内容写入 SQLite
- 脚本同时触发 Flask 应用的缓存刷新
- 我打开浏览器检查页面,微调格式
这套流程里,WorkBuddy 承担的是“调度中心”的角色。它的自定义指令功能让我可以把多个步骤打包成一个按钮,不用每次都在终端里敲命令。对于日更来说,这种效率提升是实实在在的。
2. 环境搭建与核心工具安装
2.1 Python 环境与 Flask 安装
第一步肯定是把 Python 装好。我推荐用 3.10 或以上的版本,因为 Flask 3.x 对 Python 版本有要求,而且新版本在性能和类型提示上都有改进。Windows 用户直接去官网下载安装包,记得勾选“Add Python to PATH”。macOS 用户可以用 Homebrew,Linux 用户用系统包管理器或者 pyenv 都行。
装完 Python 之后,我强烈建议用虚拟环境。这不是矫情,而是为了避免不同项目之间的依赖冲突。命令很简单:
python -m venv venv source venv/bin/activate # Linux/macOS venv\Scripts\activate # Windows激活之后,安装 Flask 和 SQLite 相关的库:
pip install flask flask-sqlalchemySQLite 本身不需要额外安装,Python 标准库自带 sqlite3 模块。但如果你想像我一样用 ORM 来操作数据库,flask-sqlalchemy 会让代码更整洁。当然,你也可以直接写原生 SQL,性能会稍微好一点,但可维护性差一些。
注意:如果你在 Windows 上遇到 sqlite3 编译问题,大概率是缺少 Visual C++ Build Tools。装一个 Visual Studio Build Tools 就能解决,不需要装完整的 Visual Studio。
2.2 SQLite 数据库与可视化工具
SQLite 的数据库就是一个文件,默认后缀是 .db 或 .sqlite。你可以在 Flask 项目里指定路径,比如instance/site.db。创建数据库不需要单独的命令,第一次运行 Flask 应用时,SQLAlchemy 会自动建表。
但日常维护中,你肯定需要查看和修改数据。这时候一个可视化工具就很重要了。我试过好几个,最后留在电脑上的是 DB Browser for SQLite。它免费、开源、跨平台,支持直接打开 .db 文件,可以浏览表结构、执行 SQL、导出 CSV。对于日更型站点来说,偶尔需要手动改一篇文章的发布时间或者修正一个错别字,用 DB Browser 比写 SQL 快得多。
安装方式也很简单,去官网下载对应系统的安装包,一路下一步就行。打开之后,点击“打开数据库”,选择你的 .db 文件,就能看到所有表和数据。
提示:DB Browser for SQLite 有一个“执行 SQL”标签页,你可以把常用的查询语句保存下来,比如“查询最近 7 天发布的文章”,下次直接点一下就能运行。
2.3 WorkBuddy 的安装与初始配置
WorkBuddy 的安装取决于你的操作系统。Windows 和 macOS 都有图形化安装包,Linux 用户可以通过命令行安装。安装完成后,第一次启动会让你选择工作目录,我建议单独建一个文件夹,比如~/workbuddy-projects,把所有和站点相关的脚本、配置、数据库都放在里面。
接下来是配置自定义指令。WorkBuddy 的指令系统支持多种触发方式,可以是一个按钮,也可以是一个快捷键。我建了三个基础指令:
- 新建草稿:弹出一个输入框,让我填标题和正文,然后调用 Python 脚本写入数据库
- 发布文章:把草稿状态改为已发布,同时更新发布时间
- 刷新缓存:向 Flask 应用发送一个内部请求,清除页面缓存
这些指令的背后都是 Python 脚本,WorkBuddy 负责传递参数和触发执行。如果你不熟悉脚本编写,WorkBuddy 也提供了一些内置模板,可以先从模板改起。
3. Flask 应用的核心结构设计
3.1 项目目录与蓝图划分
Flask 没有强制目录结构,但为了日更维护方便,我建议按功能划分蓝图。我的目录大概长这样:
site/ ├── app/ │ ├── __init__.py │ ├── models.py │ ├── routes/ │ │ ├── main.py │ │ ├── admin.py │ │ └── api.py │ ├── templates/ │ └── static/ ├── instance/ │ └── site.db ├── scripts/ │ └── workbuddy_tasks.py └── config.pymodels.py放 SQLAlchemy 的模型定义,routes/下面按模块拆分路由,templates/放 Jinja2 模板,static/放 CSS、JS 和图片。scripts/专门放 WorkBuddy 调用的脚本,和 Web 应用本身解耦,方便单独测试。
这种结构的好处是,当你想加一个新功能时,只需要新建一个蓝图文件,然后在__init__.py里注册一下,不会影响到现有代码。对于日更型站点来说,内容相关的路由和后台管理路由分开,也能避免权限混乱。
3.2 数据库模型设计
我的内容模型比较简单,但覆盖了日更所需的核心字段:
class Article(db.Model): id = db.Column(db.Integer, primary_key=True) title = db.Column(db.String(200), nullable=False) slug = db.Column(db.String(200), unique=True, nullable=False) summary = db.Column(db.String(500)) content = db.Column(db.Text, nullable=False) status = db.Column(db.String(20), default='draft') created_at = db.Column(db.DateTime, default=datetime.utcnow) published_at = db.Column(db.DateTime) updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)slug是 URL 里用的唯一标识,我一般用标题的拼音或者英文翻译,加上日期前缀,比如2024-01-15-python-flask-setup。status区分草稿和已发布,published_at单独记录发布时间,方便按时间排序。
如果你要做数据可视化,比如农产品价格走势,可以再加一个PriceData模型,用外键关联到文章或者单独存在。SQLite 对日期时间的处理比较宽松,我建议统一用 UTC 存储,展示时再转成本地时间。
3.3 路由与模板渲染
主路由负责展示文章列表和详情:
@main.route('/') def index(): page = request.args.get('page', 1, type=int) articles = Article.query.filter_by(status='published')\ .order_by(Article.published_at.desc())\ .paginate(page=page, per_page=10) return render_template('index.html', articles=articles) @main.route('/article/<slug>') def article_detail(slug): article = Article.query.filter_by(slug=slug, status='published').first_or_404() return render_template('article.html', article=article)模板用 Jinja2 继承一个基础模板,把导航、页脚、样式抽出来。日更型站点的模板不需要太复杂,重点是加载速度和移动端适配。我用的 CSS 框架是 Pico.css,体积极小,默认样式就很好看,不需要额外配置。
提示:Flask 的
first_or_404()是一个很实用的快捷方法,找不到记录时自动返回 404 页面,省去了手动判断的代码。
4. WorkBuddy 日更流程的实操记录
4.1 从草稿到发布的完整链路
我每天的操作从 WorkBuddy 工作台开始。点击“新建草稿”按钮后,会弹出一个表单,让我填标题和正文。标题我一般直接写中文,WorkBuddy 会自动调用一个 Python 函数生成 slug——如果标题是中文,就用拼音库转成拼音,再加日期前缀。
正文我习惯用 Markdown 写,因为后面渲染成 HTML 很方便。WorkBuddy 会把标题、正文、slug 一起传给scripts/workbuddy_tasks.py里的create_draft函数。这个函数做三件事:
- 检查 slug 是否已存在,如果存在就追加一个随机后缀
- 生成摘要——取正文前 150 个字符,去掉 Markdown 标记
- 写入数据库,状态设为 draft
写完草稿后,我可以在浏览器里预览。Flask 应用有一个/admin/preview/<id>路由,只有本地访问才能看到草稿内容。确认没问题后,我再点“发布文章”按钮,WorkBuddy 调用publish_article函数,把状态改为 published,并设置published_at为当前时间。
4.2 自动化脚本的关键代码
create_draft的核心逻辑大概是这样:
def create_draft(title, content): slug = generate_slug(title) summary = content[:150].replace('#', '').replace('*', '').strip() article = Article( title=title, slug=slug, summary=summary, content=content, status='draft' ) db.session.add(article) db.session.commit() return article.idgenerate_slug用到了pypinyin库,把中文转成拼音,然后用正则去掉特殊字符,最后加上日期:
from pypinyin import lazy_pinyin import re from datetime import datetime def generate_slug(title): pinyin = ''.join(lazy_pinyin(title)) pinyin = re.sub(r'[^a-z0-9]+', '-', pinyin.lower()) date_prefix = datetime.now().strftime('%Y-%m-%d') return f"{date_prefix}-{pinyin[:50]}"这个 slug 生成策略的好处是,URL 里既有日期又有语义,对搜索引擎友好,也方便我自己在数据库里定位。如果标题特别长,截取前 50 个字符就够了,避免 URL 过长。
4.3 缓存刷新与页面更新
Flask 默认没有缓存,但如果你用了 Flask-Caching 或者 CDN,发布新文章后需要刷新缓存。我的做法是在publish_article函数末尾加一个内部请求:
import requests def publish_article(article_id): article = Article.query.get(article_id) article.status = 'published' article.published_at = datetime.utcnow() db.session.commit() # 触发缓存刷新 try: requests.get('http://127.0.0.1:5000/admin/clear-cache') except requests.exceptions.ConnectionError: pass # 本地开发环境可能没启动缓存服务 return article.slug这个请求打到 Flask 应用的一个内部路由,清除页面缓存。如果是生产环境,可以把缓存服务换成 Redis,逻辑是一样的。
注意:
requests.get默认会等待响应,如果缓存服务挂了,可能会阻塞。我建议加一个超时参数,比如timeout=2,避免 WorkBuddy 卡住。
5. 常见问题与排查技巧实录
5.1 数据库锁定与并发写入
SQLite 的一个已知限制是并发写入。如果 WorkBuddy 在写入的同时,Flask 应用也在写(比如记录访问日志),可能会遇到database is locked错误。我的解决方案是:
- 把日志写入单独的文件,不要写数据库
- WorkBuddy 的写入操作加一个重试机制,失败后等待 0.5 秒再试
- 如果并发量真的很大,考虑换成 PostgreSQL,但对于日更站来说,SQLite 足够
重试机制的代码很简单:
import time from sqlalchemy.exc import OperationalError def safe_commit(max_retries=3): for i in range(max_retries): try: db.session.commit() return True except OperationalError: db.session.rollback() time.sleep(0.5) return False5.2 Flask 获取客户端变量的类型问题
有一次我在做表单提交时,发现从request.form拿到的数据全是字符串,导致数字比较出错。Flask 的request.form和request.args默认都是字符串类型,需要手动转换。比如:
page = request.args.get('page', 1, type=int)type=int会自动转换,如果转换失败就返回默认值。对于表单里的数字字段,我建议在模型层面做验证,或者在路由里显式转换。不要依赖隐式转换,否则调试起来很痛苦。
5.3 WorkBuddy 指令不生效的排查
WorkBuddy 的自定义指令偶尔会不触发,我遇到过几次,原因各不相同:
| 现象 | 可能原因 | 解决方法 |
|---|---|---|
| 点击按钮无反应 | 脚本路径错误 | 检查 WorkBuddy 配置里的绝对路径 |
| 执行后报权限错误 | 脚本没有执行权限 | Linux/macOS 下chmod +x script.py |
| 执行成功但数据库没变化 | 虚拟环境不对 | 确保 WorkBuddy 调用的是项目虚拟环境里的 Python |
| 中文乱码 | 编码问题 | 在脚本开头加# -*- coding: utf-8 -*- |
最隐蔽的一个问题是虚拟环境。WorkBuddy 默认可能调用系统 Python,而不是你项目里的 venv。解决方法是在指令配置里写死 Python 解释器的绝对路径,比如/home/user/site/venv/bin/python。
5.4 数据库文件加密与备份
SQLite 数据库文件默认是不加密的,如果你有敏感数据,可以考虑 SQLCipher。但对于普通内容站来说,加密不是必须的。我更建议做好备份——每天发布完成后,把site.db复制一份到备份目录,用日期命名。WorkBuddy 可以加一个“备份数据库”指令,调用shutil.copy就行。
import shutil from datetime import datetime def backup_db(): src = 'instance/site.db' dst = f'backups/site-{datetime.now().strftime("%Y%m%d")}.db' shutil.copy(src, dst)这个操作几乎不耗时,但关键时刻能救命。我有一次误删了一篇文章,就是从备份里恢复的。
6. 日更站点的扩展思路与个人体会
6.1 数据可视化与 Flask 的整合
如果你的站点不只是文字,还想展示价格走势、统计图表,Flask 可以和 Plotly、Chart.js 配合。我的做法是在文章模型里加一个chart_data字段,存 JSON 格式的数据,模板里用 Chart.js 渲染。这样每篇文章可以带一个独立的图表,不需要额外的页面。
对于农产品价格这种时间序列数据,我建议单独建一个表,用日期做索引,Flask 提供一个 API 接口返回 JSON,前端用 fetch 获取数据并绘图。这样数据和内容分离,更新起来更灵活。
6.2 从 SQLite 迁移到其他数据库的时机
SQLite 不是万能的。当你遇到以下情况时,就该考虑迁移了:
- 日均写入超过 1000 次
- 需要多台服务器同时读写
- 数据库文件超过 1GB
- 需要复杂的用户权限管理
迁移到 PostgreSQL 或 MySQL 并不难,SQLAlchemy 的 ORM 层基本不用改,只需要换连接字符串和驱动。但迁移之前,一定要做好数据导出和验证,避免字段类型不兼容。
6.3 我踩过的坑与最后的小技巧
第一个坑是时区。我一开始用datetime.now(),结果服务器在 UTC 时区,发布时间总是差 8 小时。后来统一用datetime.utcnow(),展示时再转本地时间。
第二个坑是 slug 冲突。有两篇文章标题一样,slug 就重复了。我的解决方法是加一个随机后缀,或者用文章 ID 做后缀。
第三个坑是 WorkBuddy 的指令缓存。有时候改了脚本,WorkBuddy 还在用旧的缓存。重启 WorkBuddy 或者手动清除指令缓存就能解决。
最后分享一个小技巧:在 Flask 的模板里加一个{{ article.updated_at }}的显示,这样读者能看到文章最后更新时间。对于日更站点来说,这个细节能增加信任感。另外,把 WorkBuddy 的“新建草稿”指令绑定一个全局快捷键,比如 Ctrl+Alt+N,随手就能开始写,灵感来了不会断。