我有以下代码:
#!/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 创建一个新的 User
与 user
的内容匹配的实例。那不是我的目标。事实上,我很难相信这种行为会有用——至少我没有用。我希望能够长寿User
记录在数据库中并在任意时间创建新的 Thing
指的是User
。在我展示的示例中,我等待 User
被创建,但在实际代码中它可能是通过 User.findOne()
重新加载的.
我见过other questions和 answers说我必须显式指定隐式创建的 UserId
我的专栏Thing.create()
称呼。当 Sequelize 提供像 Thing.belongsTo(User)
这样的 API 时,我不应该必须意识到 Thing.UserId
字段已创建。那么创建新的 Thing
的干净的 API 尊重方式是什么?它指的是特定的 User
无需猜测 UserId
的名称字段?当我加载Thing
时并指定{include: [User]}
,我通过 thing.User
访问加载的用户属性(property)。我认为我不应该知道或尝试访问 thing.UserId
field 。在我的Thing.belongsTo(User)
打电话,我从来没有指定 UserId
,我只是将其视为我不应该关心的实现细节。在创建 Thing
时,如何继续避免关心该实现细节? ?
Thing.create()
调用有效,但对我来说看起来不对:
Thing.create({
name: 'A thing',
UserId: user.id
});
最佳答案
选项 1 - 存在数据库不一致的风险
Sequelize 动态生成用于在实例上设置关联的方法,例如thing.setUser(用户);
。在您的用例中:
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 - 不起作用/有问题
它没有记录,但从代码潜水我认为以下应该有效。事实并非如此,但这似乎是因为一些错误:
// ...
.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.build
替换 models.User.create
不起作用,因为已构建但未保存的实例的主键为 null。 Instance#_setInclude
如果实例的主键为 null,则忽略该实例。
选项 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 的脚本 here
关于node.js - 在sequelize 中创建行时如何引用关联而不假定外键列名称?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34059081/