I have a problem creating foreign key onDelete constraint using Sequelize on MSSQL db.
This is my use case: each company can have multiple users and those users can create multiple requests. Some requests can be created without a reference to the user, which is why I have both references to company and user in the request model. I want to achieve that once a company is deleted, all users from the company are deleted and all requests from the company are deleted.
These are my models defined in Sequelize:
Company model:
const CompanyModel = sequelize.define('company', {
... some props ...
});
CompanyModel.associate = function (models) {
CompanyModel.hasMany(models.user, {
as: 'users',
foreignKey: 'companyId',
onDelete: 'CASCADE',
onUpdate: 'CASCADE',
});
CompanyModel.hasMany(models.request, {
as: 'requests',
foreignKey: 'companyId',
onDelete: 'CASCADE',
onUpdate: 'CASCADE',
});
};
User model:
const UserModel = sequelize.define('user', {
... some props ...
});
UserModel.associate = function (models) {
UserModel.belongsTo(models.company, {
as: 'company',
foreignKey: 'companyId',
});
UserModel.hasMany(models.request, {
as: 'requests',
foreignKey: 'userId',
});
};
Request model:
const RequestModel = sequelize.define('request', {
... some props ...
});
RequestModel.associate = function (models) {
RequestModel.belongsTo(models.company, {
as: 'company',
foreignKey: 'companyId',
});
RequestModel.belongsTo(models.user, {
as: 'user',
foreignKey: 'userId',
});
};
However, when I try to sync my database, this is the error I'm getting:
RequestError: Could not create constraint or index. See previous errors
'IF OBJECT_ID(\'[request]\', \'U\') IS NULL CREATE TABLE [request] ([id] CHAR(36) , ... some props ..., [companyId] CHAR(36) NULL, [userId] CHAR(36) NULL, PRIMARY KEY ([id]), FOREIGN KEY ([companyId]) REFERENCES [company] ([id]) ON DELETE CASCADE, FOREIGN KEY ([userId]) REFERENCES [user] ([id]) ON DELETE SET NULL);'
If I comment the relationship to the user inside of the request model then syncing of the databases passes. The same thing happens if I comment onDelete foreign key constraint inside of the company model for the user. The foreign key constraint for request doesn't matter. I guess, the problem is that deleting company will delete user which might be the creator of the request. The logic is that those requests will be deleted as well, but the database cannot know if only users from the same company (which will be deleted) will be the owners of requests deleted as well for the company.
I've also tried setting allowNull to foreign key for the user in request model, but it didn't work.
Any suggestions on what should I do? I need to achieve that deleting a company deletes its users and requests.
Related
I am trying to get a query of all merchants associated with a user.
There are three tables:
user_tbl, user_favorite_tbl, and merchant_tbl
For each, I have created a Sequelize instance.
User, UserFavorite, and Merchant.
I am trying to do a query that will get me, per user, every merchant favorited via the user_favorite_tbl.
Here is my code:
public async fetch_user_with_favorite() {
let favMerchants = await User.findAll({
attributes: ["given_name", "family_name", "email"],
include: [
{
model: UserFavorite,
attributes: ["merchant_id"],
include: [
{
model: Merchant,
attributes: ["name"]
}
]
}
]
});
return favMerchants;
}
And
User.belongsToMany(Merchant, { through: "user_favorite_tbl" });
Merchant.belongsToMany(User, { through: "user_favorite_tbl" });
However when I try to call the end point associated with this code, I get the following error:
SequelizeEagerLoadingError: user_favorite_tbl is not associated to user_tbl!
When I try to add this:
User.hasMany(UserFavorite, { foreignKey: "user_id" });
Merchant.hasMany(UserFavorite, { foreignKey: "merchant_id" });
I get this error:
SequelizeEagerLoadingError: merchant_tbl is not associated to user_favorite_tbl!
Where exactly am I going wrong?
You can define associations as below -
Many to many relationships can be achieved using two one to many relationships as below-
User.hasMany(UserFavorite, { foreignKey: 'user_id', targetKey: 'id'});
UserFavorite.hasMany(User,{ foreignKey: 'user_id',targetKey: 'id'});
Merchant.hasMany(UserFavorite, { foreignKey: 'merchant_id', targetKey: 'id'});
UserFavorite.hasMany(Merchant,{ foreignKey: 'merchant_id',targetKey: 'id'});
Your belongsToMany association looks correct, however, when you have belongsToMany, your findAll should not have the middle table in include. through is taking care of the middle table association internally.
let favMerchants = await User.findAll({
attributes: ["given_name", "family_name", "email"],
include: [
{
model: Merchant,
attributes: ["name"],
through: { attributes: [] } // if you do not need any metadata in user_favorite_tbl.
}
]
});
On a side note
User.belongsToMany(Merchant, { through: "user_favorite_tbl" });
This maybe better to use
User.belongsToMany(Merchant, { through: UserFavorite });
Im trying to create association between two tables using models that are generated by sequelize-cli. But it is not making the relations in db.
Model User
User.hasMany(models.Albums, {
foreignKey: 'user_id',
as: 'userAlbums'
});
};
Model Album
Albums.associate = function(models) {
// associations can be defined here
Albums.belongsTo(models.User, {
foreignKey: 'user_id',
onDelete: 'cascade'
});
};
What I want to do is to make the relationships that User sell or buy Item. To do it with Sequelize and MySQL, I wrote code like this.
db.User.hasMany(db.Item);
db.Item.belongsTo(db.User, { as: "buyer" });
db.User.hasMany(db.Item);
db.Item.belongsTo(db.User, { as: "seller" });
However, this code makes an error. I think db.User.hasMany(db.Item) creates userId in Item, and the code make that happen twice. But the problem is, I don't want to add userId in Item, but buyerId and sellerId. How do I need to set options in db.User.hasMany(db.Item)?
Given that you have buyerId and sellerId columns in Item, this should do:
db.User.hasMany(db.Item);
db.Item.belongsTo(db.User, { as: "buyer", targetKey: "buyerId"});
db.Item.belongsTo(db.User, { as: "seller", targetKey: "sellerId" });
If not User.id is the primary key, you should also add foreignKey: [key name] to both options.
See reference manual
You can create two 1-M relationships between User and Item like this:
db.User.hasMany(db.Item, {
foreignKey: 'sellerId',
});
db.User.hasMany(db.Item, {
foreignKey: 'buyerId',
});
db.Item.belongsTo(db.User, {
foreignKey: 'sellerId',
constraints: false,
as: 'seller'
});
db.Item.belongsTo(db.User, {
foreignKey: 'buyerId',
constraints: false,
as: 'buyer'
});
I am trying to order by the count of the associated model, and have been getting the following error from Sequelize
Cannot read property '_modelAttribute' of undefined
I have following code:
db.user.belongsToMany(db.user, {
as: "User",
through: "UserFollowers",
foreignKey: "user_id"
});
db.user.belongsToMany(db.user, {
as: "Follower",
through: "UserFollowers",
foreignKey: "follower_id"
});
and
User.findAll({
attributes:["username",db.sequelize.literal('(SELECT COUNT(*) FROM "public"."Users","public"."UserFollowers" WHERE "Users".user_id = "UserFollowers".user_id GROUP BY "Users".user_id)'),'followercount'],
include:[{model:User,as:'Follower'}],
order:[{ model: User, as: 'Follower' }[db.sequelize.literal('followercount')],'DESC']
}).then(users => {
res.json(users)
})
I appreciate any help!
From glancing at the code, the issue seems to be on the ordering clause.
Change it to:
order: [[{ model: User, as: 'Follower' }, 'followercount', 'DESC']]
If that doesn't cut it, try logging the outcome of the query by adding logging: console.log. That should help you identify what part is failing by analyzing the sql query.
I have 3 models. User, Profile and comments.
Profile is an association of User (one to one) and comments are an association of Profile (one to many).
User Model:
attributes: {
profile: {
model: 'Profile'
},
}
Profile Model:
attributes: {
comments: {
collection: 'profileComment',
via: 'profile'
}
}
Comments model:
attributes: {
profile: {
model: 'Profile'
},
}
Getting the user profile works fine:
User.findOneById(id)
.populate('profile')
.exec(function (err, user) {
// user.profile
});
But then how would I populate the profile with the comments?
It seems like you could back into what you want by setting a user attribute on profile:
attributes: {
comments: {
collection: 'profileComment',
via: 'profile'
},
user: {
model: 'User'
}
}
And then querying with:
Profile.findOne({user: userId})
.populate('user')
.populate('comments')
.exec(function(err, profile) {
// use profile.user and profile.comments
});
Keep in mind, however, that Waterline doesn't currently implement true one-to-one associations, so if you set a Profile instance's user attribute to 123, the corresponding User instance won't automatically have its profile attribute set. This may not be a big deal--you can always look up Profile and populate User, like in the example above--but it's something to keep in mind.
Your other option is to keep things as they are and do a mapping, as in this question and answer.