gpt4 book ai didi

node.js - Sequelize 和羽毛 : When Relationships Fall Apart

转载 作者:搜寻专家 更新时间:2023-10-31 23:20:25 26 4
gpt4 key购买 nike

在尝试弄清楚为什么我的 Sequelize 模型不致力于他们的关系两天之后,我决定是时候向大家征求意见了。

这是故事。

我正在使用以 Sequelize 作为驱动程序的 Postgres (9.4) 数据库编写 Feathers JS 应用程序。我在 Feathers Docs 中完成了设置,经过一番劝说,我开始运行迁移。

据我所知,必须特别考虑使用 Sequelize 获得双向关系,因为如果 ModelA 引用 ModelBModelB必须已经定义,但是如果 ModelB 引用 ModelA...那么,我们就会遇到依赖循环。

正是由于文档所说的“使用此处描述的方法定义您的模型”的依赖循环。 (好吧,从技术上讲,它只是“假设”使用了这样的结构。另外,我只能发布 2 个链接,否则我会链接那个傻瓜。对此感到抱歉。)我在 Feathers demo 中找到了相同的结构。 .

自然地,我反射(reflect)了所有这些(当然,除非我遗漏了一个小但重要的细节),但是......仍然没有骰子。

这是我正在查看的内容:

迁移

migrations/create-accounts.js

'use strict';

module.exports = {
up: function (queryInterface, Sequelize) {
// Make the accounts table if it doesn't already exist.
// "If it doesn't already exist" because we have the previous migrations
// from Laravel.
return queryInterface.showAllTables().then(function(tableNames) {
if (tableNames.accounts === undefined) {
queryInterface.createTable('accounts', {
// Field definitions here
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
},
name: Sequelize.STRING,
url_name: Sequelize.STRING,
createdAt: {
type: Sequelize.DATE,
allowNull: false
},
updatedAt: {
type: Sequelize.DATE,
allowNull: false
},
deletedAt: Sequelize.DATE
});
}
});

// See the create-user migration for an explanation of why I
// commented out the above code.
},

down: function (queryInterface, Sequelize) {
return queryInterface.dropTable('accounts');
}
};

migrations/create-users.js

'use strict';

module.exports = {
up: function (queryInterface, Sequelize) {
return queryInterface.showAllTables().then(function(tableNames) {
if (tableNames.users === undefined) {
queryInterface.createTable('users', {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
},
accountId: {
type: Sequelize.INTEGER,
references: {
model: 'accounts',
key: 'id'
},
allowNull: false
},
email: {
type: Sequelize.STRING,
allowNull: false
},
[...]
});
}
});
},

down: function (queryInterface, Sequelize) {
return queryInterface.dropTable('users');
}
};

psql

然后我启动了 psql 来查看引用是否正确:

databaseName=#\d accounts:

Referenced by:
TABLE "users" CONSTRAINT "users_accountId_fkey" FOREIGN KEY ("accountId") REFERENCES accounts(id)

databaseName=#\d 用户:

Foreign-key constraints:
"users_accountId_fkey" FOREIGN KEY ("accountId") REFERENCES accounts(id)

到目前为止还不错吧?

让我们看看这个程序的模型部分!

模型

src/models/account.js

'use strict';

// account-model.js - A sequelize model
//
// See http://docs.sequelizejs.com/en/latest/docs/models-definition/
// for more of what you can do here.

const Sequelize = require('sequelize');

module.exports = function(app) {
// We assume we're being called from app.configure();
// If we're not, though, we need to be passed the app instance.
// Fair warning: I added this bit myself, so it's suspect.
if (app === undefined)
app = this;
const sequelize = app.get('sequelize');

// The rest of this is taken pretty much verbatim from the examples
const account = sequelize.define('account', {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
},
name: Sequelize.STRING,
url_name: Sequelize.STRING,
}, {
paranoid: true,
timestamps: true,

classMethods: {
associate() {
const models = app.get('models');
this.hasMany(models['user'], {});
}
}
});

return account;
};

src/models/user.js

'use strict';

// user-model.js - A sequelize model
//
// See http://docs.sequelizejs.com/en/latest/docs/models-definition/
// for more of what you can do here.

const Sequelize = require('sequelize');

module.exports = function(app) {
// We assume we're being called from app.configure();
// If we're not, though, we need to be passed the app instance
if (app === undefined)
app = this;
const sequelize = app.get('sequelize');

const user = sequelize.define('user', {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
},
accountId: {
type: Sequelize.INTEGER,
references: {
model: 'accounts', // Table name...is that right? Made the migration work...
key: 'id'
}
},
email: Sequelize.STRING,
[... curtailed for brevity ...]
}, {
// Are these necessary here, or just when defining the model to make a
// psuedo-migration?
paranoid: true, // soft deletes
timestamps: true,

classMethods: {
associate() {
const models = app.get('models');
// This outputs like I'd expect:
// Just to be sure...From the user model, models["account"]: account
console.log('Just to be sure...From the user model, models["account"]:', models['account']);
this.belongsTo(models['account'], {});
}
}
});

return user;
};

src/models/index.js

// I blatantly ripped this from both the following:
// https://github.com/feathersjs/generator-feathers/issues/94#issuecomment-204165134
// https://github.com/feathersjs/feathers-demos/blob/master/examples/migrations/sequelize/src/models/index.js

const Sequelize = require('sequelize');
const _ = require('lodash');

// Import the models
const account = require('./account');
const user = require('./user');

module.exports = function () {
const app = this;

// Note: 'postgres' is found in config/default.json as the db url
const sequelize = new Sequelize(app.get('postgres'), {
dialect: app.get('db_dialect'),
logging: console.log
});
app.set('sequelize', sequelize);

// Configure the models
app.configure(account);
app.configure(user);

app.set('models', sequelize.models);

// Set associations
Object.keys(sequelize.models).forEach(modelName => {
if ('associate' in sequelize.models[modelName]) {
sequelize.models[modelName].associate();
}
});

sequelize.sync();

// Extra credit: Check to make sure the two instances of sequelize.models are the same...
// Outputs: sequelize.models after sync === app.get("models")
// I've also run this comparison on sequelize and app.get('sequelize'); _.eq() said they also were identical
if (_.eq(sequelize.models, app.get('models')))
console.log('sequelize.models after sync === app.get("models")');
else
console.log('sequelize.models after sync !== app.get("models")');
};

齐心协力

src/app.js

为了简洁起见,我从中删除了很多内容,我将模型加载到 app 中,如下所示:

const models = require('./models')
app.use(compress())
// Lots of other statements
.configure(models);

测试

我一直在尝试制作一个命令行实用程序来更改密码、修改用户权限和其他实用程序任务,所以我使用了 Vorpal(同样,只有 2 个链接,所以你必须自己查找如果你不熟悉——抱歉)。以下是我的 Vorpal 程序的相关片段:

cli.js

const vorpal = require('vorpal')();
const _ = require('lodash');

// Initialize app
// This seems a bit overkill since we don't need the server bit for this, but...
const app = require('./src/app');
const models = app.get('models');

// Get the models for easy access...
const User = models['user'];
const Account = models['account'];

// Run by issuing the command: node cli test
// Outputs to terminal
vorpal.command('test', 'A playground for testing the Vorpal environment.')
.action(function(args, callback) {
// User.belongsTo(Account); // <-- uncomment this and it works
User.findOne({ include: [{ model: Account }]}).then((user) => {
console.log("user.account.name:", user.account.name);
});
});

vorpal.show().parse(process.argv);

问题

抱歉这么久才到这里,但我不知道这里面的哪一部分是相关的,所以我只好吐了。

运行 node cli test 给我一个错误

Just to be sure...From the user model, models["account"]: account
sequelize.models after sync === app.get("models")
connect:
Unhandled rejection Error: account is not associated to user!
at validateIncludedElement (/vagrant/node_modules/sequelize/lib/model.js:550:11)
at /vagrant/node_modules/sequelize/lib/model.js:432:29
at Array.map (native)
at validateIncludedElements (/vagrant/node_modules/sequelize/lib/model.js:428:37)
at .<anonymous> (/vagrant/node_modules/sequelize/lib/model.js:1364:32)
at tryCatcher (/vagrant/node_modules/bluebird/js/release/util.js:16:23)
at Promise._settlePromiseFromHandler (/vagrant/node_modules/bluebird/js/release/promise.js:504:31)
at Promise._settlePromise (/vagrant/node_modules/bluebird/js/release/promise.js:561:18)
at Promise._settlePromise0 (/vagrant/node_modules/bluebird/js/release/promise.js:606:10)
at Promise._settlePromises (/vagrant/node_modules/bluebird/js/release/promise.js:685:18)
at Async._drainQueue (/vagrant/node_modules/bluebird/js/release/async.js:138:16)
at Async._drainQueues (/vagrant/node_modules/bluebird/js/release/async.js:148:10)
at Immediate.Async.drainQueues (/vagrant/node_modules/bluebird/js/release/async.js:17:14)
at runCallback (timers.js:574:20)
at tryOnImmediate (timers.js:554:5)
at processImmediate [as _immediateCallback] (timers.js:533:5)

啊!

但是,如果我取消注释 User.findOne() 正上方的行,它会像一个魅力。

为什么我必须在查询关系之前立即明确设置关系?为什么在用户模型的 associate() 方法中建立的关系(大概)没有坚持?据我所知,它正在被调用——而且是在正确的模型上。它是否以某种方式被覆盖? app 是否出于某种奇怪的原因,在建立关联时在用户模型中与在 cli.js 中不同?

我真的很困惑。非常感谢你们能提供的任何帮助。

最佳答案

我不知道为什么会这样,但我确实通过进行以下更改使其正常工作。

src/models/index.js

我在导出函数末尾附近注释掉了以下 block :

Object.keys(sequelize.models).forEach(modelName => {
if ('associate' in sequelize.models[modelName]) {
sequelize.models[modelName].associate();
}
});

然后我将它移动到 src/relate-models.js 中:

src/relate-models.js

/**
* This is workaround for relating models.
* I don't know why it works, but it does.
*
* @param app The initialized app
*/
module.exports = function(app) {
const sequelize = app.get('sequelize');

// Copied this from src/models/index.js
Object.keys(sequelize.models).forEach(modelName => {
if ('associate' in sequelize.models[modelName]) {
sequelize.models[modelName].associate();
}
});
}

src/app.js 中,我调用了那个函数,然后......很快就成功了。

src/app.js`

const models = require('./models')
app.use(compress())
// Lots of other statements
.configure(models);

require('./relate-models')(app);

结束。如果有人能解释为什么以后做完全相同的事情会奏效,请告诉我,但现在……它奏效了。

关于node.js - Sequelize 和羽毛 : When Relationships Fall Apart,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39151050/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com