我有以下代码:
#!/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
属性访问加载的用户。我认为我不应该知道或尝试访问thing.UserId
字段。在Thing.belongsTo(User)
调用中,我从未指定UserId
,我只是将其视为一个我不应该关心的实现细节。在创建Thing
?
有效的Thing.create()
电话,但对我来说不合适:
Thing.create({
name: 'A thing',
UserId: user.id
});
答案 0 :(得分:1)
选项1 - 存在数据库不一致的风险
Sequelize动态生成用于设置实例关联的方法,例如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 - 无效/错误
它没有记录,但从代码潜水中我认为以下内容应该有效。它没有,但似乎是因为一些错误:
// ...
.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.create
替换models.User.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();
});
我上传了一个脚本演示&#39选项2和选项3 here
答案 1 :(得分:0)
在 sequelize@6.5.1 sqlite3@5.0.2
上测试我可以使用 User.associations.Comments.foreignKey
:
const Comment = sequelize.define('Comment', {
body: { type: DataTypes.STRING },
});
const User = sequelize.define('User', {
name: { type: DataTypes.STRING },
});
User.hasMany(Comment)
Comment.belongsTo(User)
console.dir(User);
await sequelize.sync({force: true});
const u0 = await User.create({name: 'u0'})
const u1 = await User.create({name: 'u1'})
await Comment.create({body: 'u0c0', [User.associations.Comments.foreignKey]: u0.id});
在创建过程中也会返回关联,因此您还可以:
const Comments = User.hasMany(Comment)
await Comment.create({body: 'u0c0', [Comments.foreignKey]: u0.id});
在多对多的表中,第二个外键得到 foreignKey
和 otherKey
。
User.associations.Comments.foreignKey
包含 foreignKey
UserId
。
或者类似地使用别名:
User.hasMany(Post, {as: 'authoredPosts', foreignKey: 'authorId'});
Post.belongsTo(User, {as: 'author', foreignKey: 'authorId'});
User.hasMany(Post, {as: 'reviewedPosts', foreignKey: 'reviewerId'});
Post.belongsTo(User, {as: 'reviewer', foreignKey: 'reviewerId'});
await sequelize.sync({force: true});
// Create data.
const users = await User.bulkCreate([
{name: 'user0'},
{name: 'user1'},
])
const posts = await Post.bulkCreate([
{body: 'body00', authorId: users[0].id, reviewerId: users[0].id},
{body: 'body01', [User.associations.authoredPosts.foreignKey]: users[0].id,
[User.associations.reviewedPosts.foreignKey]: users[1].id},
])
但是那个语法太长了,我很想在任何地方对键进行硬编码。