Feathers.js/Sequelize->服务两个模型之间的关系 [英] Feathers.js / Sequelize -> Service with relations between two models

查看:71
本文介绍了Feathers.js/Sequelize->服务两个模型之间的关系的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经通过sequelize使feathers.js与mysql一起运行.这正在工作,我可以从表中收集数据.下一步是在模型中定义联接".

我有一个表,其列为'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中做到这一点?

解决方案

好,我已经对代码做了一些调整.为了使每个人都容易理解,我将逐步介绍实际的表示例.我有一个表"sections"和一个表"categories"(这是一个更简单的示例).该部分具有带有类别的外键.因此,这是到目前为止我所做的:

category-model.js

classMethods: {
    associate() {
        category.hasOne(sequelize.models.sections, {
            as: 'category',
            foreignKey: 'category_id'
        });
    },
},

section-model.js

classMethods: {
    associate() {
        section.belongsTo(sequelize.models.categories, {
            allowNull: true
        });
    },
},

services \ 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网络风暴.因此,我进行了npm启动,并且我的表现在具有正确的外键,因此该部分可以正常工作.同样,数据显示仍然正确.获取各节的查询仍在工作.如果在index.js中使用不同的编码,则npm start不会失败,但是部分查询失败.

接下来是:钩子.这就是我现在有些困惑的地方.一些网站说它在查找定义"中(现在叫它,但这在我的vue组件安装部分中).然后,您对它进行了解释,并很好地展示了该内容包括在该部分中,但是它什么也没做,代码仍然可以运行,但是当我通过邮递员获得这些部分时,我看不到类别信息.然后,我将得到例如

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');".如前所述,它什么也不做.因此,跳回到我在这里得到的解释,它说"..从前钩..".我找到了用于类别和服务的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插件并展开"listSections",这是"serviceSections.find"的结果.我希望在其中看到类别列,但也许那是错误的期望.我在调试器的选择中也没有看到加入"

稍后再

好吧,所以我搞砸了.最终,我还遇到了有关如何从多个数据库中检索数据的帖子许多关系.阅读此书,我发现调整"hooks \ index.js"的意图是正确的,但这意味着我的代码不正确.因此,尝试对该帖子进行不同的组合以及上面提供的提示,我现在有了这个

section \ hooks \ 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进行邮递员工作的原因,因为将其放在之前"部分的获取"部分中,并且它在VUE中也起作用,因为我将函数放在了之前"的查找"部分中.

多重连接

好的,我需要多次加入部分".我也有身份.这来自我的元数据表.因此,这意味着对元数据模型中的部分进行相同的关联.我是这样做的:

metadata-model.js

classMethods: {
    associate() {
        metadata.hasOne(sequelize.models.sections, {
            as: 'satus',
            foreignKey: 'status_id',
            targetKey: 'status_id'
        });
    }, },

在这里,我开始始终放置ForeignKey属性. targetKey是另一个表格中的列名.如果需要更改它,非常方便. "as"属性是一个别名,我喜欢至少在多次使用它时几乎总是使用它.在截面模型中,我进行了更改.

section-model.js

classMethods: {
    associate() {
        section.belongsTo(sequelize.models.categories, {
            allowNull: false,
            as: 'category'
        });
        section.belongsTo(sequelize.models.metadata, {
            allowNull: false,
            as: 'status'
        });
    }, },

为完成这一点,我更改了hooks函数.我也尝试过函数,但是那没用,所以我将两者合并了.

section \ hooks \ 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,所以它确实是一对一的,但是通过这种方式,如果由于某种原因元数据条目更改为其他类型,我仍然可以在"status"上进行选择,并且可以对不正确的行发出警告. /p>

最后一部分将是您想要内部连接还是左侧外部连接.我是由hooks部分中的'required:true'属性触发的.

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;
    })
}

我在这里所做的是从数组中过滤掉不需要的列.我还重命名了一些. '* _en'是多语言的,因此我需要使用一个变量.再次重复执行include,以从联接中获取相关列.

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.

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.

What do I need to do to make this in feathters.js?

解决方案

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:

category-model.js

classMethods: {
    associate() {
        category.hasOne(sequelize.models.sections, {
            as: 'category',
            foreignKey: 'category_id'
        });
    },
},

section-model.js

classMethods: {
    associate() {
        section.belongsTo(sequelize.models.categories, {
            allowNull: true
        });
    },
},

services\index.js

...
app.set('models', sequelize.models);
...
Object.keys(sequelize.models).forEach(function(modelName) {
    if ("associate" in sequelize.models[modelName]) {
        sequelize.models[modelName].associate();
    }
});

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.

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 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 hooks\index.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);
        }
    }],
    ...
};

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).

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

Edit some time later:

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 "hooks\index.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

section\hooks\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);
    };
}

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'.

Mutliple Joins

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:

metadata-model.js

classMethods: {
    associate() {
        metadata.hasOne(sequelize.models.sections, {
            as: 'satus',
            foreignKey: 'status_id',
            targetKey: 'status_id'
        });
    }, },

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

classMethods: {
    associate() {
        section.belongsTo(sequelize.models.categories, {
            allowNull: false,
            as: 'category'
        });
        section.belongsTo(sequelize.models.metadata, {
            allowNull: false,
            as: 'status'
        });
    }, },

To finalize this, I changed the hooks function. I first tried too functions, but that didn't work, so I merged the two.

section\hooks\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);
    };
}

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.

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 part

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;
    })
}

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.

这篇关于Feathers.js/Sequelize->服务两个模型之间的关系的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆