Ari
Ari

Reputation: 4999

Sequelize - How To Design One To One Association?

I am creating two models, the first one is User and the second one is Portfolio. When a user is creating a Portfolio (where a user can only have one Portfolio), I want it to have reference to the user who is creating it, and every time that user's data is fetched, I want it to also fetching their portfolio data if any.

I am trying to use hasOne to create portfolio_id inside User tables, with the skeleton generated using sequelize init command, but it is not working. I cannot find a column the name protfolio_id if I don't put it inside the user migration file. Is that how it is supposed to be?

How should I design the models? Should I include the portfolio_id in User tables and include user_id in Portfolio table, or is there a best way to do it?

And which associations method should I use, hasOne or belongsTo?

Upvotes: 0

Views: 6221

Answers (1)

Hussain Nawaz Lalee
Hussain Nawaz Lalee

Reputation: 896

First of all make sure that you are calling Model.associate for each model. This will run queries for all the relationships.

You can define the relationships in the associate method as follows:

// user.js (User Model definition)
module.exports = (sequelize, dataTypes) => {
    const { STRING } = dataTypes
    const User = sequelize.define("user", {
        username: { type: STRING }
    })

    User.associate = models => {
        User.hasOne(models.Portfolio, { foreignKey: "userId" }) // If only one portfolio per user
        User.hasMany(models.Portfolio) // if many portfolios per user
    }

    return User
}

// portfolio.js (Portfolio Model definition)
module.exports = (sequelize, dataTypes) => {
    const { STRING } = dataTypes
    const Portfolio = sequelize.define("portfolio", {
        portfolioName: { type: STRING }
    })

    Portfolio.associate = models => {
        Portfolio.belongsTo(models.User, { foreignKey: "userId" })
    }

    return Portfolio
}

hasOne stores the foreignKey in the target model. So this relationship will add a foreign key userId to the Portfolio model.

belongsTo stores the key in the current model and references the primary key of the target model. In this case the Portfolio.belongsTo will add userId in the Portfolio model which will reference the primary key of User model.

Notice how both these relationships do the same thing, they add userId to the Portfolio model. Its better to define this in both models for your last use case:

I want it to have reference to the user who is creating it, and every time that user's data is fetched, I want it to also fetching their portfolio data if any.

Accessing related models:

In sequelize fetching a related model together with the main model is called Eager Loading. Read more about it here.

Now for your use case if you want to fetch the portfolio if any, while fetching user, do the following:

var userWithPortfolio = await User.findAll({include: [models.Portfolio]};
// Or you may also use include: {all: true} to include all related models.
var userWithPortfolio = await User.findAll({include: {all: true}};

/*  
    Output:

    userWithPortfolio = {
        username: "xyz",
        portfolio: {
            portfolioName: "xyz"
        }
    }
*/

Upvotes: 3

Related Questions