Beego ORM中default与null同时设为true会导致default失效,因插入时跳过该字段,仅靠数据库DEFAULT约束或BeforeInsert钩子才能可靠实现默认值。

Beego ORM中default和null同时设为true会失效
Beego ORM(v1.x)在定义模型字段时,若同时设置 default 和 null=true,实际插入数据时 default 值不会被写入数据库,而是直接存 NULL——哪怕你显式调用 o.Insert() 且未赋值该字段。这是因为 Beego 在生成 INSERT SQL 时,对 null=true 字段默认跳过字段名,不参与值绑定,default 也就无从生效。
常见错误现象:CreatedTime *time.Time `orm:"null;type(datetime);default(now)"` 在 MySQL 中始终插入 NULL,而非当前时间。
- 只设
null=true:字段可为空,但无默认行为,插入时必须显式传值或留空(留空即 NULL) - 只设
default(不加null):字段非空,未赋值时用 default 填充(如default(0)、default("N/A")) - 想让字段可空 + 有默认值?Beego ORM 不支持语义级“默认但允许 NULL”,只能靠数据库层兜底
MySQL 层面用 DEFAULT + NULL 是唯一可靠方案
Beego ORM 的 default 标签本质只是生成建表语句的提示,不参与运行时逻辑。真正起作用的是数据库自身的 DEFAULT 约束。所以正确做法是:模型里去掉 default,只保留 null=true,然后手动确保建表 SQL 包含 DEFAULT 子句。
例如,对 status 字段希望默认为 "pending" 但允许为空:
type Order struct {
Id int `orm:"auto"`
Status string `orm:"null;size(20)"`
}
再通过以下方式之一补上数据库默认值:
- 手动执行:
ALTER TABLE order CHANGE status status VARCHAR(20) DEFAULT 'pending' NULL; - 用
orm.RunSyncdb()前,先调用orm.RegisterModelWithPrefix并自定义建表 SQL(需重写TableEngine或 patchgenerateCreateSQL) - 上线前统一用 migration 工具(如 golang-migrate)管理 DDL,避免 ORM 自动建表
orm:"null;default(...)" 在 SQLite/PostgreSQL 下表现不一致
SQLite 对 DEFAULT 支持宽松,即使字段声明 NULL,default 标签仍可能生效;PostgreSQL 则更严格,null=true 会导致字段被标记为 NULLABLE,但 ORM 插入时不带字段名,DB 层 default 不触发。这种差异会让本地开发(SQLite)和生产(MySQL/PG)行为不一致。
关键点:
- 不要依赖
default标签跨数据库生效 - 测试环境尽量与生产 DB 类型一致(尤其是涉及默认值、约束、时区字段)
- 对
*time.Time字段,MySQL 的now()是函数,默认值需写成default(now()),而 Beego 的default(now)会被当成字符串字面量
运行时主动控制默认值:用 BeforeInsert 替代 ORM 标签
当需要动态逻辑(比如根据用户角色设不同默认状态),或必须兼容多种数据库时,最可控的方式是放弃 default 标签,改用 Beego 的钩子方法。
func (o *Order) BeforeInsert() error {
if o.Status == "" {
o.Status = "pending"
}
if o.CreatedTime.IsZero() {
o.CreatedTime = time.Now()
}
return nil
}
注意:
-
BeforeInsert在o.Insert()调用前执行,但o.Read()或o.LoadRelated()不触发它 - 如果用
o.QueryTable("order").Filter("id", 123).Update(&data),钩子也不会执行 - 多个钩子叠加时,顺序不可控,建议单个模型只用一个
BeforeInsert做聚合判断


















