☰
从WordPress到Flask+SQLite:用WorkBuddy搭建日更农产品价格站实战
2026/9/26 21:25:40 网站建设 项目流程

1. 为什么我放弃了WordPress,转头用WorkBuddy+Flask从零搭站

去年年底我给自己定了个目标:做一个能日更的农产品价格数据小站。不是那种花架子展示页,而是每天能自动抓数据、入库、渲染图表、还能让我在后台手动改几条记录的真东西。一开始我走的是WordPress路线,插件装了一堆,采集插件、图表插件、缓存插件,结果站点越跑越慢,数据库里全是插件留下的垃圾表,改一个字段要在后台点七八层菜单。折腾了大概两周,我彻底放弃了。

后来我把目光转向了自建站方案。这里先澄清一个很多人搞混的问题:Shopify、WordPress、自建站到底有什么区别。Shopify是托管电商,你租的是它的店铺系统,数据和模板都锁在它生态里;WordPress是内容管理系统,灵活但依赖插件生态,性能上限受PHP和MySQL配置影响;而自建站是你自己写后端、自己管数据库、自己控渲染,自由度最高,代价是啥都得自己动手。我选的是第三条路,技术栈定成Flask + SQLite + Python,开发辅助工具用WorkBuddy。

为什么是WorkBuddy?因为我不是科班后端出身,写Flask的时候经常卡在“这个路由该怎么绑”“模板变量传没传进去”“SQLite的update语句为什么没生效”这种具体问题上。WorkBuddy在这类场景里更像一个随时能问的结对伙伴,它能帮我把零散的Python代码片段串成可运行的模块,也能在我贴出报错栈的时候给出排查方向。注意,它不是替你建站的一键工具,而是加速你理解和落地的辅助角色。这一点想清楚,后面才不会走偏。

这套组合适合谁?适合有一点Python基础、想拥有一个完全可控的小型数据站、并且愿意每天花二三十分钟维护的人。如果你连Python都没装过,也别慌,我会把安装和环境配置的坑一并写清楚。整篇记录围绕我真实的建站和日更流程展开,包括Flask怎么绑定到网页元素、SQLite怎么可视化管理、WorkBuddy在哪些环节真正省了时间、以及日更机制怎么设计才不会把自己累死。

2. 环境搭建:Python、VSCode与SQLite这三样必须先落地

2.1 Python安装与VSCode环境配置的实际顺序

很多人一上来就装一堆东西,结果环境变量没配好,pip用不了,后面全乱。我的建议是严格按顺序来:先装Python,再配VSCode,最后装SQLite工具。Python安装教程网上满天飞,但真正容易踩的坑是安装时那个“Add Python to PATH”的勾。Windows上如果你没勾,后面在命令行敲python会直接提示找不到命令,这时候要么重装,要么手动去环境变量里加路径,非常麻烦。我建议直接去官网下最新稳定版,安装界面第一个勾务必打上。

装完之后验证三步:命令行敲python --version看版本,敲pip --version看包管理器,敲python -m venv testenv看能不能建虚拟环境。这三步都过了,说明Python本体没问题。接下来是VSCode Python环境配置。装好VSCode后,第一件事是装Python扩展,第二件事是选解释器。按Ctrl+Shift+P,输入“Python: Select Interpreter”,选中你刚装的那个Python路径。很多人代码跑不起来就是因为VSCode默认选了个别的解释器,或者根本没选。

虚拟环境这块我强烈建议每个项目单独建一个。命令是:

python -m venv venv # Windows激活 venv\Scripts\activate # macOS/Linux激活 source venv/bin/activate

激活后命令行前面会出现(venv)前缀,这时候再pip install flask,包就只装在这个项目里,不会污染全局。我见过太多人全局装了几十个包,最后版本冲突到没法收拾。

2.2 SQLite的定位:为什么小站不该上MySQL

SQLite数据库最容易被误解的一点是“它是不是太弱了”。实际上对于日更型小站,SQLite完全够用,甚至比MySQL更合适。它是文件型数据库,整个库就是一个.db文件,备份就是复制文件,迁移就是拷走文件,不需要单独起数据库服务。我的农产品价格站每天新增几百条记录,查询都是按日期和品类过滤,SQLite的响应在毫秒级,根本感受不到瓶颈。

安装方面,SQLite本身通常随Python自带,你import sqlite3就能用,不需要额外装服务端。但可视化工具必须配一个,否则你天天用命令行查数据会疯。我用的是DB Browser for SQLite,免费、跨平台、界面直观。装好之后直接打开你的.db文件,能看到所有表、字段、索引,还能直接执行SQL。这里有个细节:DB Browser修改数据后要记得点“Write Changes”,否则你的改动只在内存里,关掉就没了。我第一次用的时候就因为这个丢过一批测试数据。

如果你用Android Studio做移动端,它内置的SQLite可视化工具也能看库,但那个偏开发调试,日常管理还是DB Browser顺手。至于“SQLite数据库文件能否加密”,原生SQLite不直接支持加密,需要用到SQLCipher这类扩展,对于内部小站来说没必要,做好文件权限和备份就行。

2.3 WorkBuddy安装与它在环境阶段的真实作用

WorkBuddy安装教程的核心其实就一句话:把它当成你编辑器或终端旁边的常驻助手。我是在VSCode里配合使用的,遇到环境报错直接把错误信息贴给它。比如我第一次配虚拟环境时,Windows PowerShell提示“无法加载文件 activate.ps1,因为在此系统上禁止运行脚本”,这个问题卡了我半小时。WorkBuddy给我的排查路径是:先确认执行策略,用Get-ExecutionPolicy查看,如果是Restricted,就用Set-ExecutionPolicy -Scope CurrentUser RemoteSigned放开当前用户。这个思路比我自己瞎搜快多了。

需要说明的是,WorkBuddy有国际版,也有和CodeBuddy并列的产品线,功能侧重不太一样。CodeBuddy更偏代码补全和生成,WorkBuddy更偏任务编排和流程辅助。我在建站阶段主要用WorkBuddy来梳理“下一步该做什么”,比如它会提醒我先建数据库schema再写路由,而不是反过来。这个顺序很重要,先有数据结构再有接口,改起来成本低。

3. 数据库设计:用DB Browser把表结构一次定清楚

3.1 农产品价格表到底该存哪些字段

建站最容易返工的地方就是表结构。我一开始只存了品类、价格、日期三个字段,结果日更两周后发现想按产地筛选、想对比不同市场的价格,全都没法做。后来重新设计,字段扩成这些:id(主键自增)、category(品类,如白菜、土豆)、market(市场名)、origin(产地)、price(价格,用REAL类型)、unit(单位,如元/斤)、record_date(记录日期,用TEXT存ISO格式)、created_at(入库时间戳)。

为什么日期用TEXT而不是DATE类型?因为SQLite的日期函数对TEXT格式的ISO字符串支持最好,WHERE record_date = '2024-01-15'这种查询直接走索引,简单可靠。价格用REAL会有浮点精度问题,如果你要做精确的金额计算,建议用INTEGER存“分”,展示时再除以100。我这个站只是展示趋势,REAL够用。

建表语句我直接在DB Browser的“Execute SQL”里跑:

CREATE TABLE price_records ( id INTEGER PRIMARY KEY AUTOINCREMENT, category TEXT NOT NULL, market TEXT NOT NULL, origin TEXT, price REAL NOT NULL, unit TEXT DEFAULT '元/斤', record_date TEXT NOT NULL, created_at TEXT DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX idx_date_category ON price_records(record_date, category);

那个索引是关键。日更站最常见的查询就是“某品类最近30天价格”,没有索引的话数据量上万后查询会明显变慢。加索引后同样的查询基本瞬间返回。

3.2 用WorkBuddy辅助生成建表与迁移脚本

表结构定下来后,我让WorkBuddy帮我把建表语句整理成一个可重复执行的Python脚本,这样换台机器也能一键初始化。它给我的结构是:先检查表是否存在,不存在才创建,避免重复执行报错。

import sqlite3 def init_db(db_path='price.db'): conn = sqlite3.connect(db_path) cursor = conn.cursor() cursor.execute(''' CREATE TABLE IF NOT EXISTS price_records ( id INTEGER PRIMARY KEY AUTOINCREMENT, category TEXT NOT NULL, market TEXT NOT NULL, origin TEXT, price REAL NOT NULL, unit TEXT DEFAULT '元/斤', record_date TEXT NOT NULL, created_at TEXT DEFAULT CURRENT_TIMESTAMP ) ''') cursor.execute(''' CREATE INDEX IF NOT EXISTS idx_date_category ON price_records(record_date, category) ''') conn.commit() conn.close() if __name__ == '__main__': init_db() print('数据库初始化完成')

IF NOT EXISTS这个写法是精髓,它让脚本具备幂等性,跑多少次都不会出错。这个模式后来被我复用到所有初始化逻辑里。

3.3 数据录入的两种方式:手动与脚本批量

日更站的数据来源通常有两种:一种是你自己手动整理,一种是脚本抓取。手动录入我直接在DB Browser里点“New Record”填,适合少量修正。批量录入走Python脚本,用executemany一次插入多条:

records = [ ('白菜', '城东市场', '山东', 1.85, '元/斤', '2024-01-15'), ('土豆', '城西市场', '甘肃', 2.10, '元/斤', '2024-01-15'), ] conn.executemany( 'INSERT INTO price_records (category, market, origin, price, unit, record_date) VALUES (?,?,?,?,?,?)', records ) conn.commit()

用参数化查询(那些问号)而不是字符串拼接,能避免引号转义问题,也更安全。这一点WorkBuddy在我第一次写插入语句时就提醒过我,当时我还在用f-string拼SQL,遇到品类名里带单引号直接报错。

4. Flask路由与模板:把数据库里的数据绑到网页元素上

4.1 最小可运行Flask应用的骨架

Flask框架的好处是起步极简。一个能跑的应用就这几行:

from flask import Flask, render_template import sqlite3 app = Flask(__name__) @app.route('/') def index(): conn = sqlite3.connect('price.db') conn.row_factory = sqlite3.Row cursor = conn.cursor() cursor.execute('SELECT * FROM price_records ORDER BY record_date DESC LIMIT 50') rows = cursor.fetchall() conn.close() return render_template('index.html', records=rows) if __name__ == '__main__': app.run(debug=True)

conn.row_factory = sqlite3.Row这行很关键,它让查询结果可以像字典一样用字段名访问,模板里写record['category']而不是record[1],可读性天差地别。debug=True只在开发时开,它会自动重载代码并显示详细错误页,但上线必须关掉。

4.2 Flask如何绑定到网页元素:模板变量传递的完整链路

“Flask如何绑定到网页元素”这个问题,本质是问数据怎么从后端传到前端。链路是这样的:路由函数查询数据库得到rows,通过render_template的第二个参数传给模板,模板里用Jinja2语法{{ }}输出,用{% %}做循环和判断。

模板templates/index.html的核心部分:

<table> <thead> <tr> <th>品类</th><th>市场</th><th>产地</th><th>价格</th><th>日期</th> </tr> </thead> <tbody> {% for record in records %} <tr> <td>{{ record['category'] }}</td> <td>{{ record['market'] }}</td> <td>{{ record['origin'] }}</td> <td>{{ record['price'] }} {{ record['unit'] }}</td> <td>{{ record['record_date'] }}</td> </tr> {% endfor %} </tbody> </table>

这里有个新手常踩的坑:模板文件必须放在项目根目录下的templates文件夹里,名字不能错,Flask默认只去那里找。静态文件(CSS、JS)放static文件夹。我第一次把html放在根目录,怎么都渲染不出来,排查了半天。

4.3 用WorkBuddy排查“变量传了但页面不显示”的问题

我遇到过最诡异的一次是:路由里明明查到了数据,print(rows)也有输出,但页面就是空白。WorkBuddy让我按三步排查:第一,确认render_template的变量名和模板里用的名字完全一致,大小写敏感;第二,在模板里临时加{{ records }}看原始输出,如果显示的是空列表,说明查询结果为空;第三,检查数据库连接是不是连到了另一个.db文件。

结果我是第三种情况。项目目录下有两个db文件,一个是我手动建的,一个是脚本生成的,Flask连的是空的那个。这个问题在有多环境时特别常见。后来我统一用绝对路径或者基于__file__的相对路径来定位数据库文件,彻底避免。

import os BASE_DIR = os.path.dirname(os.path.abspath(__file__)) DB_PATH = os.path.join(BASE_DIR, 'price.db')

4.4 数据可视化:用Chart.js把价格趋势画出来

光有表格不够直观,我加了趋势图。前端用Chart.js,后端提供一个返回JSON的接口:

from flask import jsonify @app.route('/api/trend/<category>') def trend(category): conn = sqlite3.connect(DB_PATH) cursor = conn.cursor() cursor.execute(''' SELECT record_date, AVG(price) FROM price_records WHERE category = ? GROUP BY record_date ORDER BY record_date ''', (category,)) data = cursor.fetchall() conn.close() return jsonify({ 'labels': [row[0] for row in data], 'values': [row[1] for row in data] })

前端用fetch拿数据喂给Chart.js。这里注意,jsonify返回的格式前端要对应解析,labels和values两个数组长度必须一致,否则图表会错位。我一开始因为某天数据缺失导致长度不匹配,图直接画歪了,后来在SQL里用GROUP BY保证每天只有一条聚合记录才解决。

5. 日更机制:让站点每天自动有新内容

5.1 日更不是每天手动敲,而是设计一条流水线

“日更”听起来很勤奋,但如果每天都要手动查数据、手动录入、手动发布,坚持不了一周就会放弃。我的做法是把日更拆成三个可自动化的环节:数据获取、数据入库、页面刷新。数据获取我用Python爬虫抓公开的农产品价格信息,入库用脚本,页面因为是动态渲染的,数据一进库刷新就更新,不需要重新生成静态页。

爬虫部分要克制,只抓公开的、允许访问的页面,控制频率,不要给对方服务器造成压力。我一般每天固定时间跑一次,抓取当天的价格数据,清洗后入库。清洗包括去空格、统一单位、过滤异常值(比如价格突然是0或者几千,明显是脏数据)。

5.2 用WorkBuddy编排每日任务清单

WorkBuddy在日更环节的价值是帮我管理“今天该做什么”。我给它设了一套自定义指令,比如“检查昨日数据完整性”“生成本周价格波动摘要”“提醒备份数据库”。它会把任务拆成步骤,我照着执行就行。这种编排能力比单纯问代码问题更省心,因为它管的是流程而不是片段。

备份这块我要强调:SQLite虽然是一个文件,但备份不能简单复制正在写入的文件,可能拿到不一致的状态。正确做法是用SQLite自带的备份命令:

import sqlite3 source = sqlite3.connect('price.db') backup = sqlite3.connect('backup/price_20240115.db') source.backup(backup) backup.close() source.close()

这样得到的是完整一致的副本。我每天日更完成后自动跑一次备份,保留最近30天。

5.3 数据更新与删除:SQLite update语句的正确姿势

日更过程中难免要修正数据,比如某天价格录错了。SQLite的update语句写法:

UPDATE price_records SET price = 2.35, unit = '元/斤' WHERE id = 123;

关键在WHERE条件,漏写WHERE会更新全表,这是灾难性的。我建议在DB Browser里执行update前先跑一遍对应的SELECT,确认影响的行数对不对,再改成UPDATE。另外,如果开了事务但没commit,改动不会落盘,DB Browser里记得点Write Changes,脚本里记得conn.commit()。

删除同理,DELETE FROM price_records WHERE id = ?,永远带条件。我给自己定了个规矩:任何写操作先在测试库上跑一遍。

6. 部署上线:从本地跑通到公网可访问

6.1 Flask部署的几种方式和选择逻辑

本地app.run()只适合开发,上线要用生产级服务器。常见方案是Gunicorn(Linux)或Waitress(Windows)做WSGI服务器,前面挂Nginx做反向代理。我选的是Gunicorn+Nginx,因为部署在Linux服务器上更稳。

启动命令:

gunicorn -w 4 -b 127.0.0.1:8000 app:app

-w 4是4个工作进程,根据CPU核数调整,一般设成核数×2+1。app:app是模块名:应用实例名。Nginx那边配置反向代理到8000端口,顺便处理静态文件,减轻Flask压力。

6.2 上线前必须检查的清单

上线前我列了个清单,逐项过:debug模式必须关;数据库文件权限设成只有运行用户可读写;备份脚本挂到定时任务;日志输出到文件而不是控制台;错误页面自定义,别把堆栈暴露给用户。这几项里最容易忘的是debug模式,开着debug上线等于把服务器内部信息公开展示,非常危险。

6.3 日更站在线上的稳定性观察

上线后我观察了两周,主要看三件事:响应时间、错误日志、数据库大小增长。响应时间稳定在200毫秒以内,错误日志里偶尔有爬虫超时的记录,不影响主流程。数据库两周增长了约5MB,按这个速度一年也就100多MB,SQLite完全扛得住。如果哪天数据量真的上来了,再考虑迁移到PostgreSQL,但那是后话,现在没必要过度设计。

7. 这一路踩过的坑和WorkBuddy真正帮上忙的地方

回头看,这个站从零到日更稳定运行,花了我大概三周业余时间。最大的坑不是技术难点,而是“想太多做太少”。我一开始纠结要不要上Docker、要不要用ORM、要不要做用户系统,结果WorkBuddy提醒我:先让最小版本跑起来,再迭代。这句话点醒了我,于是我用最朴素的Flask+SQLite把核心链路打通,后面所有优化都是在这个能跑的版本上做的。

WorkBuddy真正帮上忙的场景有三个:一是环境报错的快速定位,尤其是Windows下的路径和权限问题;二是代码片段的串联,它能把散落的函数组织成有逻辑的模块;三是流程提醒,日更这种需要长期坚持的事,有个东西帮你盯着任务清单,比纯靠自律靠谱。至于“WorkBuddy从入门到精通”这种资料,我的建议是别指望看完就会,直接拿一个真实小项目练,遇到问题再查,效率高十倍。

最后分享一个我自己的小习惯:每次改完代码,先在本地跑一遍完整流程——初始化库、插入测试数据、启动服务、打开页面、点一遍所有功能。这个习惯帮我拦住了至少五次“改A坏B”的低级错误。建站这事,稳比快重要。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询