Feathers.js/续集->具有两个模型之间关系的服务
我已经通过 sequelize 让 feathers.js 与 mysql 一起运行.这是有效的,我可以从表中收集数据.下一步是在模型中定义连接".
I've got feathers.js functioning with mysql via sequelize. This is working, I can collect data from tables. Next step is to define 'joins' in the models.
我有一个包含status_id"和country_id"列的表.这些列引用元数据表中的 id.在 SQL 中我是对的:
I have a table with a column 'status_id' and 'country_id'. These columns reference to an id in a metadata table. In SQL I would right:
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')
在这种情况下,此元数据表不会很大,因此采用了这种方法.它确实提供了我需要的灵活性.
This metadata table won't be big in this case so hence this approach. It does give flexibility I need.
在feathters.js 中我需要做什么?
What do I need to do to make this in feathters.js?
推荐答案
好的,我已经对代码进行了一些调整.为了让每个人都能阅读,我将转到实际的表格示例.我有一个表'sections'和一个表'categories'(这是一个更简单的例子).该部分有一个带有类别的外键.所以这就是我到目前为止所做的:
Ok I've done some adjusting of the code. To keep things readable for everyone, I'm going to step over to actual table examples. I have a table 'sections' and a table 'categories' (this is a more simple example). The section has a foreign key with the category. So this is what I've done for that so far:
类别模型.js
classMethods: {
associate() {
category.hasOne(sequelize.models.sections, {
as: 'category',
foreignKey: 'category_id'
});
},
},
section-model.js
section-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 start,我的表现在有正确的外键,所以这部分工作.此外,数据的显示仍然正确.获取这些部分的查询仍在工作.如果我在 index.js 中有不同的编码,那么 npm start 并没有失败,但是 section 查询失败了.
I'm using Jetbrains webstorm in this case. So I did a npm start, and my table now has the correct foreign key, so that part works. Also, the display of the data is still correct. The query which gets the sections still is working. If I had a different coding in the index.js, then npm start didn't fail, but the sections query failed.
接下来是:钩子.这就是我现在感到困惑的地方.一些网站说它在查找定义"中(我们暂时称它为,但这是在我的 vue 组件安装部分中).然后你解释了,很好地展示了,包含在那部分,但它什么也没做,代码仍然运行,但是当我通过邮递员获取部分时,我没有看到类别信息.然后我会有例如
Next is up: hooks. This is where I'm getting some confusion now. Some sites say it's in the 'find definition' (let's call it that for now, but this is in my vue component mounted section). Then you it's explained, well showed, the include is in that part, but it does nothing, well the code still runs, but I don't see category information when I get the sections through postman. I will then have e.g.
serviceSections.find({
include: [{
model: serviceCategories.Model
}],
query: {
$sort: {
section_description_en: 1
}
}
}).then(page => {
page.data.reverse();
this.listSections = page.data;
})
serviceCategories 被定义为appFeathers.service('categories');".如前所述,它什么也不做.所以回到我在这里得到的解释,它说'.. from a before hook ..'.我找到了用于类别和服务的 hooksindex.js 文件.但在这里我犯了错误.我首先在类别中进行了此调整,然后在第一节中进行了调整
serviceCategories is defined as "appFeathers.service('categories');". As mentioned it does nothing. So jumping back to the explanation I had gotten here, it says '.. from a before hook ..'. I found the hooksindex.js file, for categories and for services. But here I make mistakes. I made this adjustment first in the categories on then in the sections one
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 错误.
This is giving code 500 fn.bind error.
只是想发布进度,这并不意味着我停止寻找最后一步(或缺少的步骤).
Just thought to post the progress, this doesn't mean I stop looking for the last step (or a missing step).
我忘记了一件事.我检查是否完成是在 chrome 中打开 F12,转到 VUE 插件并展开listSections",这是serviceSections.find"的结果.我希望在其中看到类别列,但也许这是错误的期望.我在调试器的选择中也没有看到加入"
I forgot one thing. My check to see if it is complete is to open F12 in chrome, go to the VUE plugin and expand 'listSections' which is the result of "serviceSections.find". I expect to see category column in it but maybe that is the wrong expectation. I also don't see a 'join' in the select of my debugger
稍后再
好吧,所以我搞砸了.最终,我还看到了这篇关于如何从多个到许多关系.读到这里,我认为调整hooksindex.js"的意图是正确的,但这意味着我的代码不是.所以尝试了该帖子的不同组合,以及上面提供的提示,我现在有了这个
Okay, so I messed around. Eventually I also came across this post on how to retrieve data from many to many relationships. Reading this, I figured the intention on adjusting the "hooksindex.js" was correct, but that meant my code wasn't. So trying different combinations of that post, and the tips provided above, I now have this
sectionhooksindex.js
sectionhooksindex.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,因为将它放在之前"部分的获取"部分,它在 VUE 中工作,因为我将函数放在之前"的查找"部分.
This is working in postman with GET, because of putting it in the 'get' part of 'before' section, and it's working in VUE for I put the function in the 'find' part of 'before'.
多重连接
好的,我需要多个连接到部分".我也有地位.这来自我的元数据表.所以这意味着对元数据模型中的部分进行相同的关联.我是这样做的:
Okay, I needed multiple joins in to the 'sections'. I have a status too. This is coming from my metadata table. So this meant making the same association to sections in the metadata-model. I did it like this:
元数据模型.js
classMethods: {
associate() {
metadata.hasOne(sequelize.models.sections, {
as: 'satus',
foreignKey: 'status_id',
targetKey: 'status_id'
});
}, },
这里我开始总是放入 foreignKey 属性.targetKey 是另一个表中列的名称.如果您需要更改它很方便.'as' 属性是一个别名,我几乎总是喜欢使用它,至少在多次使用它时是这样.在部分模型中,我对其进行了更改.
Here I started to always put in the foreignKey attribute. The targetKey is the name of the column in the other tabel. Handy if you need to change it. The 'as' attribute is an alias, I like to use this nearly always at least when using it multiple times. In the section-model I made changes to.
section-model.js
section-model.js
classMethods: {
associate() {
section.belongsTo(sequelize.models.categories, {
allowNull: false,
as: 'category'
});
section.belongsTo(sequelize.models.metadata, {
allowNull: false,
as: 'status'
});
}, },
为了最终确定这一点,我更改了 hooks 函数.我第一次尝试了 too 函数,但没有奏效,所以我将两者合并.
To finalize this, I changed the hooks function. I first tried too functions, but that didn't work, so I merged the two.
sectionhooksindex.js
sectionhooksindex.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,所以它确实是一对一的,但是如果由于某种原因元数据条目更改为不同的类型,我仍然可以选择状态",并且可以对不正确的行发出警告.
As you see I changed the function name. It is important to use the alias again, it didn't work other wise. In the metadata part, I put a filter. I don't want to load the entire table when looking up. Okay, I join on an id so it really is one on one, but this way if for some reason the metadata entry changes to a different type, I still have the selection on 'status' and can make a warning of incorrect rows.
如果您想要内部或左外部联接,最后一部分将是.我是通过 hooks 部分中的 'required: true' 属性触发的.
The final part would be if you want the inner or left outer joins. I triggered that by the 'required: true' attribute within the hooks sections.
VUE.js 部分
最后一部分是将其推送到 vue 组件.我在已安装部分执行此操作.我有这个代码.
The last part is to push it to a vue component. I do that in the mounted section. I have this code for that.
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;
})
}
我在这里做的是从数组中过滤掉不需要的列.我也重命名了一些.'*_en' 是多语言的,所以我需要为它使用一个变量.再次重复包含以从连接中获取相关列.
What I do here is filter undesired columns from the array. I also rename a few. The '*_en' ones are multilingual, so I need to use a variable for it. The include is repeated again to get related columns from the joins.
相关文章