I am attempting to output a nested relation where
Cat.hasMany(legs)
Leg.belongsTo(cat)
Leg.hasOne(paw)
paw.hasMany(leg)
Here is my Cat Model:
module.exports = (sequelize, DataTypes) => {
const Cat = sequelize.define('Cat', {
userId: {
type: DataTypes.STRING,
},
}, {});
Cat.associate = function (models) {
Cat.hasMany(models.Leg, {
foreignKey: 'catId',
as: 'legs',
});
};
return Cat;
};
My Legs Model:
module.exports = (sequelize, DataTypes) => {
const Leg = sequelize.define('Leg', {
originalValue: DataTypes.JSON,
newValue: DataTypes.JSON,
legId: DataTypes.INTEGER,
objectId: DataTypes.INTEGER,
pawId: DataTypes.INTEGER,
}, {});
Leg.associate = function (models) {
Leg.belongsTo(models.Cat, {
foreignKey: 'LegId',
onDelete: 'CASCADE',
});
Leg.hasOne(models.Paw, {
foreignKey: 'pawId',
});
};
return Leg;
};
Here is my Paw model
module.exports = (sequelize, DataTypes) => {
const Paw = sequelize.define('Paw', {
pawType: DataTypes.STRING,
}, {});
Paw.associate = function (models) {
Paw.hasMany(models.Leg, {
foreignKey: 'pawId',
as: 'paws',
});
};
return Paw;
};
Currently My code is outputting this when i query the Cat Table
[
{
"id": 1,
"userId": "2wdfs",
"createdAt": "2018-04-14T20:12:47.112Z",
"updatedAt": "2018-04-14T20:12:47.112Z",
"legs": [
{
"id": 1,
"catId": 1,
"pawId": 1,
"createdAt": "2018-04-14T20:12:54.500Z",
"updatedAt": "2018-04-14T20:12:54.500Z"
}
]
}
]
However I would like the pawType from the paws table to also be present when listing everything from the cat table. Something more along the lines of this:
[
{
"id": 1,
"userId": "2wdfs",
"createdAt": "2018-04-14T20:12:47.112Z",
"updatedAt": "2018-04-14T20:12:47.112Z",
"legs": [
{
"id": 1,
"catId": 1,
"paws" : [
{
"id": 1,
"pawType": "cute"
}
]
"createdAt": "2018-04-14T20:12:54.500Z",
"updatedAt": "2018-04-14T20:12:54.500Z"
}
]
}
]
Additionally, Here is the query I am using to retrieve the Cats.
return Cat.findAll({ include: [{ model: Leg, as: 'legs',include [{model: Paw,}], }], })
This is the error that is returning,
{ SequelizeDatabaseError: column legs->Paw.pawId does not exist
{ error: column legs->Paw.pawId does not exist
And the full SQL command
sql: 'SELECT "Cat"."id", "Cat"."userId", "Cat"."createdAt", "Cat"."updatedAt", "legs"."id" AS "legs.id", "legs"."originalValue" AS "legs.originalValue", "legs"."newValue" AS "legs.newValue", "legs"."catId" AS "legs.catId", "legs"."objectId" AS "legs.objectId", "legs"."pawId" AS "legs.pawId", "legs"."createdAt" AS "legs.createdAt", "legs"."updatedAt" AS "legs.updatedAt", "legs->Paw"."id" AS "legs.Paw.id", "legs->Paw"."paw" AS "legs.Paw.paw", "legs->Paw"."pawId" AS "legs.Paw.pawId", "legs->Paw"."createdAt" AS "legs.Paw.createdAt", "legs->Paw"."updatedAt" AS "legs.Paw.updatedAt" FROM "Cats" AS "Cat" LEFT OUTER JOIN "Legs" AS "legs" ON "Cat"."id" = "legs"."catId" LEFT OUTER JOIN "Paws" AS "legs->Paw" ON "legs"."id" = "legs->Paw"."pawId";' },
There are many issues. I'll try to address them incrementally.
1) Models By default, if you do not declare a primaryKey
, then sequelize automatically adds an id
column for you. Thus legId
isn't a useful column.
Furthermore, if you associate a model, the foreignKey
reference is added for you, thus pawId
shouldn't be declared.
Thus Legs.js
should be modified to:
module.exports = (sequelize, DataTypes) => {
var Leg = sequelize.define('Leg', {
originalValue: DataTypes.JSON,
newValue: DataTypes.JSON,
objectId: DataTypes.INTEGER // not entirely sure what this is
})
Leg.associate = function (models) {
// associations
}
return Leg
}
The above gives me the following columns in pgAdmin
:
2) Associations
The following association doesn't make sense, and should cause an error:
Leg.hasOne(Paw)
Paw.hasMany(Leg)
Unhandled rejection Error: Cyclic dependency found. Legs is dependent of itself.
Dependency chain: Legs -> Paws => Legs
Each Leg
should have one Paw
, and thus I suggest the following:
Leg.associate = function (models) {
// Leg.belongsTo(models.Cat)
Leg.hasOne(models.Paw, {
foreignKey: 'pawId',
as: 'paw'
})
}
Paw.associate = function (models) {
Paw.belongsTo(models.Leg, {
as: 'leg' // note this changed to make more sense
foreignKey: 'pawId'
})
}
3) Foreign Keys
Leg.belongsTo(models.Cat, {
foreignKey: 'catId', // this should match
onDelete: 'CASCADE'
})
Cat.hasMany(models.Leg, {
foreignKey: 'catId', // this should match
as: 'legs'
})
4) Eager Loading
When eager loading nested associations, you have to include
them. You should also use as
alias that matches your model associations:
Cat.findAll({
include: [{
model: Leg,
as: 'legs', // Cat.legs
include: [{
model: Paw,
as: 'paw' // Leg.paw instead of Leg.pawId
}]
}]
})
Using this entire setup and the above query, I obtain:
[
{
"id": 1,
"userId": "1",
"createdAt": "2018-04-15T11:22:59.888Z",
"updatedAt": "2018-04-15T11:22:59.888Z",
"legs": [
{
"id": 1,
"originalValue": null,
"newValue": null,
"objectId": null,
"createdAt": "2018-04-15T11:22:59.901Z",
"updatedAt": "2018-04-15T11:22:59.901Z",
"catId": 1,
"paw": {
"id": 1,
"pawType": null,
"createdAt": "2018-04-15T11:22:59.906Z",
"updatedAt": "2018-04-15T11:22:59.906Z",
"pawId": 1
}
}
]
}
]
Extra
Because this is obviously a practice setup, you could modify Paw
to be a belongsToMany
relation (perhaps you have conjoined cats by the paw?) as follows:
Paw.associate = function (models) {
Paw.belongsToMany(models.Leg, {
foreignKey: 'pawId',
through: 'PawLegs // a through join table MUST be defined
})
}
This would be the correct way to implement what you initially tried to with
Leg.hasOne(paw)
paw.hasMany(leg)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With