根本原因是buffalo db create不读config/database.yml,只认DB_URL环境变量或models.NewDB()返回的连接串;未导出DB_URL或NewDB()返回nil会导致connection refused或panic。

buffalo db create 为什么连不上数据库
根本原因不是命令写错了,而是 buffalo db create 完全不读 config/database.yml,它只认 DB_URL 环境变量或 models.NewDB() 返回的有效连接串。如果没导出 DB_URL,或者 NewDB() 返回了 nil,就会报 connection refused 或直接 panic。
常见踩坑点:
-
export DB_URL="postgres://user:pass@localhost:5432/myapp_development?sslmode=disable"必须在 shell 中执行,buffalo db不加载.env文件 - PostgreSQL 用户必须有
CREATEDB权限,否则报permission denied to create database -
go mod tidy没跑全,缺失github.com/gobuffalo/pop/v6会导致NewDB()静默返回nil - Docker Compose 中只配了
database.yml,但没在 service 的environment:下显式写DB_URL=...
测试环境该用 SQLite 还是 PostgreSQL
别在测试里复用开发数据库——污染风险高、启动慢、依赖外部服务。Buffalo 测试推荐用内存 SQLite(sqlite3://:memory:)做轻量隔离,但要注意:buffalo db 命令本身不支持 SQLite(pop v6+ 已移除 dialect 注册),所以建库和迁移不能靠它。
正确做法是绕过 CLI,直接在测试代码里初始化:
- 在
database_test.go中用pop.NewConnection(&pop.ConnectionDetails{URL: "sqlite3://:memory:"})构建连接 - 确保已导入驱动:
_ "github.com/mattn/go-sqlite3"(写在models/models.go的init()函数里) - 每个测试函数用
tx := db.Transaction(func(tx *pop.Connection) error { ... })包裹,事务自动回滚,无需手动清理 - 避免用文件路径如
./test.db,多协程并发跑测试会触发database is locked
Pop 查询在测试中查不到刚插入的数据
这是事务上下文错配的典型表现:你在事务 tx1 里 Create() 了一条记录,却用另一个连接(比如全局 models.DB 或新 pop.Connect())去 Find(),自然查不到——因为未提交的数据对其他连接不可见。
测试中必须统一使用同一个事务实例:
- 用
tx.Find(&u, u.ID)而不是models.DB.Find(&u, u.ID) - 别在 handler 里混用
c.Value("tx")和models.DB,二者生命周期和提交状态完全独立 - 若需验证“跨请求可见性”,得让前一个事务
Commit(),或改用SELECT ... FOR UPDATE加锁(仅限 PostgreSQL) - 测试中想跳过 DB 直接测逻辑?改用
buffalo.Request构造假请求,绕过真实路由和数据库调用
怎么让测试数据在每次运行前自动重置
Buffalo 没有内置的 before each 钩子,也不能靠 buffalo test 自动管理数据库状态。真正的控制权在 Go 测试函数本身。
推荐结构化处理:
- 在测试函数开头调用
tx.RawQuery("DELETE FROM users").Exec()清空表(仅限 SQLite/PostgreSQL) - 用
tx.Transaction()包裹整个测试逻辑,利用自动回滚天然实现“每次干净” - 避免在
TestMain里全局建库或跑 migration——不同测试包可能需要不同 schema,硬共享易冲突 - 如果模型含
BeforeCreate等钩子,记得用tx.ValidateAndSave(&m)替代tx.Create(),否则验证失败不会暴露
测试最常被忽略的一点:事务回滚只保证数据不落盘,但不重置序列(如 PostgreSQL 的 serial 主键)。连续跑多次 TestUserCreate,ID 可能从 1 → 2 → 3 一直递增——这本身不是 bug,但如果你的测试逻辑依赖 ID 值(比如断言 u.ID == 1),就会偶然失败。











