如何在不假定外键列名的情况下在sequelize中创build行时引用关联?

我有以下代码:

#!/usr/bin/env node 'use strict'; var Sequelize = require('sequelize'); var sequelize = new Sequelize('sqlite:file.sqlite'); var User = sequelize.define('User', { email: Sequelize.STRING}); var Thing = sequelize.define('Thing', { name: Sequelize.STRING}); Thing.belongsTo(User); sequelize.sync({force: true}).then(function () { return User.create({email: 'asdf@example.org'}); }).then(function (user) { return Thing.create({ name: 'A thing', User: user }, { include: [User] }); }).then(function (thing) { return Thing.findOne({where: {id: thing.id}, include: [User]}); }).then(function (thing) { console.log(JSON.stringify(thing)); }); 

我得到以下输出:

 ohnobinki@gibby ~/public_html/turbocase1 $ ./sqltest.js Executing (default): INSERT INTO `Users` (`id`,`email`,`updatedAt`,`createdAt`) VALUES (NULL,'asdf@example.org','2015-12-03 06:11:36.904 +00:00','2015-12-03 06:11:36.904 +00:00'); Executing (default): INSERT INTO `Users` (`id`,`email`,`createdAt`,`updatedAt`) VALUES (1,'asdf@example.org','2015-12-03 06:11:36.904 +00:00','2015-12-03 06:11:37.022 +00:00'); Unhandled rejection SequelizeUniqueConstraintError: Validation error at Query.formatError (/home/ohnobinki/public_html/turbocase1/node_modules/sequelize/lib/dialects/sqlite/query.js:231:14) at Statement.<anonymous> (/home/ohnobinki/public_html/turbocase1/node_modules/sequelize/lib/dialects/sqlite/query.js:47:29) at Statement.replacement (/home/ohnobinki/public_html/turbocase1/node_modules/sqlite3/lib/trace.js:20:31) 

似乎指定{include: [User]}指示Sequelize创build一个与user内容匹配的新User实例。 那不是我的目标。 事实上,我很难相信这样的行为永远是有用的 – 我至less没有用处。 我希望能够在数据库中有长时间的Userlogging,并且在任意时间创build引用UserThing 。 在我所示的例子中,我等待User被创build,但在实际的代码中,它可能已经通过User.findOne()新鲜加载。

我已经看到其他问题和答案,说我必须显式指定我的Thing.create()调用隐式创buildUserId列。 当Sequelize提供像Thing.belongsTo(User)这样的API时,我应该意识到Thing.UserId字段被创build的事实。 那么创build一个新的Thing指向一个特定的User而不必猜测UserId字段的名称的干净的API方式是什么? 当我加载一个Thing并指定{include: [User]} ,我通过thing.User属性访问加载的用户。 我不认为我应该知道或尝试访问一个thing.UserId字段。 在我的Thing.belongsTo(User)调用,我从来没有指定UserId ,我只是把它看作是我不应该关心的实现细节。 在创buildThing时,我怎样才能继续避免关注这个实现细节?

Thing.create()调用,但看起来不对我来说:

 Thing.create({ name: 'A thing', UserId: user.id }); 

选项1 – 风险因素不一致

Sequelizedynamic生成用于设置实例关联的方法,例如thing.setUser(user); 。 在你的用例中:

 sequelize.sync({force: true}) .then(function () { return Promise.all([ User.create({email: 'asdf@example.org'}), Thing.create({name: 'A thing'}) ]); }) .spread(function(user, thing) { return thing.setUser(user); }) .then(function(thing) { console.log(JSON.stringify(thing)); }); 

选项2 – 不工作/越野车

这是没有logging,但从代码跳水,我认为以下应该工作。 它不是,但似乎是由于一些错误:

 // ... .then(function () { return models.User.create({email: 'asdf@example.org'}); }) .then(function(user) { // Fails with SequelizeUniqueConstraintError - the User instance inherits isNewRecord from the Thing instance, but it has already been saved return models.Thing.create({ name: 'thingthing', User: user }, { include: [{ model: models.User }], fields: ['name'] // seems nec to specify all non-included fields because of line 277 in instance.js - another bug? }); }) 

使用models.User.buildreplacemodels.User.create不起作用,因为构build但未保存的实例主键为空。 Instance#_setInclude的主键为空,则忽略实例。

选项3

在事务中包装事物的create防止了不一致的状态。

 sq.sync({ force: true }) .then(models.User.create.bind(models.User, { email: 'asdf@example.org' })) .then(function(user) { return sq.transaction(function(tr) { return models.Thing.create({name: 'A thing'}) .then(function(thing) { return thing.setUser(user); }); }); }) .then(print_result.bind(null, 'Thing with User...')) .catch(swallow_rejected_promise.bind(null, 'main promise chain')) .finally(function() { return sq.close(); }); 

我已经在这里上传了一个脚本演示选项2和选项3