Bookshelf.JS问题:关联模型在require前调用引发异常
解决Bookshelf.js中循环引用导致的
Address.forge is not a function错误 你的问题核心就是循环模块引用搞的鬼——当customer.js尝试require('./address')时,address.js又反过来require('./customer'),这时候Customer模型还没完成初始化,导致两边互相拿到的都是未完全加载的空对象。虽然registry插件帮你解决了关联定义时的循环引用问题,但在customCreate方法里直接用require导入模型的方式,还是逃不过Node.js模块循环加载的坑。
下面给你两个可行的解决方案,优先推荐第一个:
方案1:利用Bookshelf Registry获取模型
既然已经启用了registry插件,就别再用require直接导入模型了,改用bookshelf.model('ModelName')来获取已经注册到registry里的模型,这样就能完美避开循环加载的问题。
修改customers.js:
// customers.js const bookshelf = require('../bookshelf'); // 删掉这行直接require Address的代码 // const Address = require('./address'); const Customer = bookshelf.Model.extend({ tableName: 'customers', addresses: function () { return this.hasMany('Address'); }, }, { customCreate: function (attributes) { return this.forge(attributes) .save() .tap(c => { // 从registry中获取Address模型 const Address = bookshelf.model('Address'); // 注意:这里最好提取地址专属的属性,别直接把客户属性全传进去 return Address.forge({ // 假设你的地址字段是这些,根据实际表结构调整 street: attributes.street, city: attributes.city, zipcode: attributes.zipcode, customer_id: c.get('id') }).save(); }) } }); module.exports = bookshelf.model('Customer', Customer);
再修改address.js,同样删掉直接require Customer的代码:
// address.js const bookshelf = require('../bookshelf'); // 删掉这行直接require Customer的代码 // const Customer = require('./customer'); const Address = bookshelf.Model.extend({ tableName: 'addresses', customer: function () { return this.belongsTo('Customer'); } }); module.exports = bookshelf.model('Address', Address);
方案2:调整模型加载顺序(不推荐)
你可以先确保Address模型被完全加载后,再加载Customer模型——比如在项目入口文件里先require('./models/address'),再require('./models/customer')。但这种方式太死板,后续模型关联变复杂后很容易出问题,所以还是优先用方案1。
额外优化建议
- 数据一致性:建议用事务包裹客户和地址的创建操作,确保两者要么都创建成功,要么都回滚,避免出现客户创建了但地址没创建的情况:
customCreate: function (attributes) { return bookshelf.transaction(t => { return this.forge(attributes) .save(null, { transacting: t }) .tap(c => { const Address = bookshelf.model('Address'); const addressAttrs = { street: attributes.street, city: attributes.city, zipcode: attributes.zipcode, customer_id: c.get('id') }; return Address.forge(addressAttrs).save(null, { transacting: t }); }); }); }
- 属性过滤:别直接把客户的所有属性传给
Address.forge(),只提取地址表需要的字段,避免把无关数据存进去。
内容的提问来源于stack exchange,提问作者asap
相关产品推荐
相关产品推荐

