我有通过sequelize使用mysql运行的feathers.js。这是有效的,我可以从表中收集数据。下一步是在模型中定义“连接”。
我有一个包含'status_id'和'country_id'列的表格。这些列引用元数据表中的id。在SQL中我会说得对:
SELECT status.description, country.description, detail
FROM details
INNER JOIN metadata status
ON (details.status_id = status.id AND status.type = 'status'
INNER JOIN metadata country
ON (details.country_id =country.id AND country.type = 'country')
此元数据表在这种情况下不会很大,因此这种方法。它确实提供了我需要的灵活性。
在feathters.js中我需要做些什么?
答案 0 :(得分:13)
在帮助很多人解决同样的问题后,我了解到解决方案分为两部分:
#1 - 拥抱ORM
大多数人的问题来自缺乏对续集的理解。为了帮助您,您首先需要了解sequelize associations如何工作以及如何使用"include" option(又名“急切加载”)执行查询。我建议几次阅读这些链接的所有内容,然后再多一次以获得良好的衡量标准;这是续集学习曲线中最陡峭的部分。如果您从未使用过ORM,请让它为您做很多繁重的工作!
#2 - 从羽毛钩设置续集选项
一旦你理解了“包含”选项如何与sequelize一起使用,你就会想要从羽毛中的“before”钩子中设置该选项。 Feathers will pass the value of hook.params.sequelize
as the options parameter用于所有续集方法调用。这就是你的钩子的样子:
// GET /my-service?name=John&include=1
function (hook) {
if (hook.params.query.include) {
const AssociatedModel = hook.app.services.fooservice.Model;
hook.params.sequelize = {
include: [{ model: AssociatedModel }]
};
// delete any special query params so they are not used
// in the WHERE clause in the db query.
delete hook.params.query.include;
}
return Promise.resolve(hook);
}
在引擎盖下方,羽毛会将您的模型find
方法称为:
// YourModel is a sequelize model
const options = Object.assign({ where: { name: 'John' }}, hook.params.sequelize);
YourModel.findAndCount(options);
<强>值得注意的:强>
旧的v1.x羽毛生成器(2017年3月之前)不会生成对sequelize友好的代码。这已在新的v2.x生成器中修复。如果您距离2017年3月之前的项目很远,那么不要使用新的生成器。请加入Slack Channel并加入sequelize
会议室寻求帮助。我一直关注那里的事情,可以帮助你。如果你刚刚开始你的项目并且还没有走得太远,那么我强烈建议从新的发电机开始。运行此命令(并按照these instructions):
$ feathers --version # see what version you are using
$ npm install -g @feathersjs/cli # install latest version of the CLI
答案 1 :(得分:4)
截至Sequelize第4版,classMethods
属性已被删除See this reference。这意味着@ Edgar的答案中的例子将不起作用。
associate
方法必须直接添加到模型中,而不是包含在classMethods
属性中。来自Sequelize文档:
一个:
const Model = sequelize.define('Model', { ... }, { classMethods: { associate: function (model) {...} }, instanceMethods: { someMethod: function () { ...} } });
新:
const Model = sequelize.define('Model', { ... }); // Class Method Model.associate = function (models) { ...associate the models }; // Instance Method Model.prototype.someMethod = function () {..}
答案 2 :(得分:2)
好的,我已经完成了一些代码调整。为了让所有人都能阅读,我将逐步介绍实际的表格示例。我有一张桌子&#39;部分&#39;和表格类别&#39; (这是一个更简单的例子)。该部分具有该类别的外键。所以这就是我迄今为止所做的:
类别的model.js
classMethods: {
associate() {
category.hasOne(sequelize.models.sections, {
as: 'category',
foreignKey: 'category_id'
});
},
},
部-model.js
classMethods: {
associate() {
section.belongsTo(sequelize.models.categories, {
allowNull: true
});
},
},
服务\ index.js
...
app.set('models', sequelize.models);
...
Object.keys(sequelize.models).forEach(function(modelName) {
if ("associate" in sequelize.models[modelName]) {
sequelize.models[modelName].associate();
}
});
我在这种情况下使用Jetbrains webstorm。所以我做了一个npm开始,我的表现在有正确的外键,所以这部分工作。此外,数据的显示仍然是正确的。获取部分的查询仍然有效。如果我在index.js中有不同的编码,那么npm start并没有失败,但是部分查询失败了。
接下来是:钩子。这就是我现在感到困惑的地方。一些网站表示它已经找到了定义&#39; (现在让我们称之为,但这是在我的vue组件安装部分)。然后你解释说,好的,包括在那部分,但它没有做任何事情,代码仍然运行,但是当我通过邮递员获得部分时,我不会看到类别信息。我会那样的。
serviceSections.find({
include: [{
model: serviceCategories.Model
}],
query: {
$sort: {
section_description_en: 1
}
}
}).then(page => {
page.data.reverse();
this.listSections = page.data;
})
serviceCategories定义为&#34; appFeathers.service(&#39; categories&#39;);&#34;。如上所述它什么也没做。所以回到我在这里得到的解释,它说是从前钩子开始......&#39;。我找到了hooks \ index.js文件,用于类别和服务。但在这里我犯了错误。我在第一节
中的类别中首先进行了此调整exports.before = {
...
find: [{
function (hook) {
if (hook.params.query.include) {
const AssociatedModel = hook.app.services.category.Model;
hook.params.sequelize = {
include: [{ model: AssociatedModel }]
};
}
return Promise.resolve(hook);
}
}],
...
};
这给代码500 fn.bind错误。
只是想发布进度,这并不意味着我不再寻找最后一步(或缺少一步)。
我忘记了一件事。我检查它是否完整是在Chrome中打开F12,转到VUE插件并展开&#39; listSections&#39;这是&#34; serviceSections.find&#34;的结果。我希望在其中看到类别列,但也许这是错误的期望。我也没有看到加入&#39;在选择我的调试器
以后编辑一段时间:
好的,所以我搞砸了。最后我还发现了这篇文章on how to retrieve data from many to many relationships。读到这个,我想到了调整&#34; hooks \ index.js&#34;的意图。这是正确的,但这意味着我的代码不是。所以尝试不同的帖子组合,以及上面提供的提示,我现在有了这个
部\钩\ index.js
...
exports.before = {
all: [],
find: [
getCategory()
],
get: [
getCategory()
],
create: [],
update: [],
patch: [],
remove: []
};
...
function getCategory() {
return function (hook) {
const category = hook.app.services.categories.Model;
hook.params.sequelize = {
include: [{ model: category }]
};
return Promise.resolve(hook);
};
}
这是在邮递员的GET工作,因为把它放在&#39; get&#39;之前的一部分&#39;部分,它在VUE工作,我把功能放在&#39;发现&#39; &之前的一部分&#39;。
Mutliple加入
好的,我需要多次加入&#39;部分&#39;。我也有一个地位。这来自我的元数据表。所以这意味着与元数据模型中的部分建立相同的关联。我是这样做的:
元数据model.js
classMethods: {
associate() {
metadata.hasOne(sequelize.models.sections, {
as: 'satus',
foreignKey: 'status_id',
targetKey: 'status_id'
});
}, },
这里我开始总是输入foreignKey属性。 targetKey是另一个表格中列的名称。如果你需要改变它很方便。 &#39; as&#39;属性是一个别名,我喜欢使用它几乎总是至少在多次使用它时。在部分模型中,我对。进行了更改。
部-model.js
classMethods: {
associate() {
section.belongsTo(sequelize.models.categories, {
allowNull: false,
as: 'category'
});
section.belongsTo(sequelize.models.metadata, {
allowNull: false,
as: 'status'
});
}, },
为了最终确定这一点,我改变了钩子功能。我第一次尝试了太多的功能,但是没有用,所以我合并了两个。
部\钩\ index.js
function getRelatedInfo() {
return function (hook) {
hook.params.sequelize = {
include: [
{
model: hook.app.services.categories.Model,
as: 'category'
},{
model: hook.app.services.metadata.Model,
as: 'status',
where: {
type: 'status'
}
}
]
};
return Promise.resolve(hook);
};
}
如您所见,我更改了功能名称。重新使用别名很重要,它没有其他方面的工作。在元数据部分,我放了一个过滤器。我不想在查找时加载整个表格。好的,我加入了一个id,所以它真的是一对一的,但是这样一来,如果由于某种原因元数据条目变为另一种类型,我仍然可以选择&#39; status&#39;并且可以警告错误的行。
最后一部分是你想要内部或外部连接。我通过&#39; required:true&#39;触发了它。钩子部分中的属性。
VUE.js部分
最后一部分是将其推送到vue组件。我在安装部分这样做。我有这个代码。
const socket = io();
const appFeathers = feathers()
.configure(feathers.socketio(socket))
.configure(feathers.hooks());
const serviceSections = appFeathers.service('sections');
const serviceArticles = appFeathers.service('articles');
const serviceMetadata = appFeathers.service('metadata');
...
mounted() {
serviceArticles.find({
include: [{
model: serviceMetadata.Model,
as: 'country',
query: {
$select: [
'country_icon'
]
}
}],
query: {
$sort: {
article_description_en: 1
},
$select: [
'id',
['article_description_en', 'article_description'],
'article_length',
'article_ascend',
'article_code'
]
}
}).then(page => {
this.listTrails = page.data;
})
}
我在这里做的是从数组中过滤不需要的列。我也重命名了一些。 &#39; * _ en&#39;一些是多语言的,所以我需要使用一个变量。再次重复包含以从连接中获取相关列。