sequelize - Cannot add foreign key constraint - javascript

I'm trying to set a 1:1 relation between two tables. RefreshToken table will have two foreignKey releated to Users table, as in this image:
I used sequelize-auto to generate my sequelize models.
Users model:
module.exports = function(sequelize, DataTypes) {
return sequelize.define('Users', {
idUsers: {
type: DataTypes.INTEGER(11),
allowNull: false,
primaryKey: true
},
name: {
type: DataTypes.STRING(45),
allowNull: true
},
mail: {
type: DataTypes.STRING(45),
allowNull: false,
primaryKey: true
}
}, {
tableName: 'Users'
});
};
RefreshToken model:
module.exports = function(sequelize, DataTypes) {
return sequelize.define('RefreshToken', {
idRefreshToken: {
type: DataTypes.INTEGER(11),
allowNull: false,
primaryKey: true
},
token: {
type: DataTypes.TEXT,
allowNull: true
},
userId: {
type: DataTypes.INTEGER(11),
allowNull: false,
primaryKey: true,
references: {
model: 'Users',
key: 'idUsers'
}
},
userEmail: {
type: DataTypes.STRING(45),
allowNull: false,
primaryKey: true,
references: {
model: 'Users',
key: 'mail'
}
}
}, {
tableName: 'RefreshToken'
});
};
When I run the application, I receive this error:
Unhandled rejection Error: SequelizeDatabaseError:
ER_CANNOT_ADD_FOREIGN: Cannot add foreign key constraint
I tried to add explicit the relation, adding in Users table:
User.associate = (models) => {
User.hasOne(models.RefreshToken, {
foreignKey: 'userId'
});
User.hasOne(models.RefreshToken, {
foreignKey: 'userEmail'
});
};
and in RefreshToken:
RefreshToken.associate = (models) => {
RefreshToken.belongsTo(models.Users, {
foreignKey: 'userId'
});
RefreshToken.belongsTo(models.Users, {
foreignKey: 'userEmail'
});
};
But I receive again the same error. If I remove the references in the RefreshToken table I don't see any error, but when I check the database I don't see any foreign key relation constraint with email and id of the User

This is common type error mainly occurs because of
1. When the primary key data type and the foreign key data type did not matched
return sequelize.define('RefreshToken', {
userId: {
type: DataTypes.INTEGER(11), // The data type defined here and
references: {
model: 'Users',
key: 'idUsers'
}
},
return sequelize.define('Users', {
idUsers: {
type: DataTypes.INTEGER(11), // This data type should be the same
},
2. When the referenced key is not a primary or unique key.
You can not have two primary keys, so other referenced keys should be defined unique. unique:true
return sequelize.define('Users', {
idUsers: {
primaryKey: true
},
mail: {
type: DataTypes.STRING(45),
allowNull: false,
primaryKey: true // You should change this to 'unique:true'. you cant hv two primary keys in one table.
}

I see two issues:
No table should contain two primary keys and userId shouldn't be in integer it should be a UUID.
I had a foreign key set to INT and it gave me error:
Unhandled rejection SequelizeDatabaseError: foreign key constraint
"constraint_name_here" cannot be implemented
Try changing:
userId: {
type: DataTypes.INTEGER(11),
allowNull: false,
primaryKey: true,
references: {
model: 'Users',
key: 'idUsers'
}
},
To
userId: {
type: DataTypes.UUID,
allowNull: false,
foreignKey: true,
references: {
model: 'Users',
key: 'idUsers'
}
},

Related

Trying to create new row in table. Getting "column 'accountId' does not exist". Looks like a new column is being returned. I don't need this column

I'm working in insomnia trying to create a new account entry.
I'm getting the following line in terminal. At the end you can see that accountId column is returned and I don't need it.
Executing (default): INSERT INTO "accounts" ("id","name","userId","type","limit","balance","createdAt","updatedAt") VALUES (DEFAULT,$1,$2,$3,$4,$5,$6,$7) RETURNING "id","name","userId","type","limit","balance","minPayment","dueDate","createdAt","updatedAt","accountId";
Because of it I get this error in insomnia:
column "accountId" does not exist
Below is the Account model
'use strict'
const { Model } = require('sequelize')
module.exports = (sequelize, DataTypes) => {
class Account extends Model {
/**
* Helper method for defining associations.
* This method is not a part of Sequelize lifecycle.
* The `models/index` file will call this method automatically.
*/
static associate(models) {
Account.hasMany(models.Transaction, { foreignKey: 'accountId' })
Account.belongsTo(models.User, { foreignKey: 'userId' })
}
}
Account.init(
{
name: {
type: DataTypes.STRING,
allowNull: false
},
userId: {
type: DataTypes.INTEGER,
onDelete: 'CASCADE',
references: {
model: 'users',
key: 'id'
}
},
type: {
type: DataTypes.INTEGER,
allowNull: false
},
limit: {
type: DataTypes.INTEGER,
allowNull: false
},
balance: {
type: DataTypes.INTEGER,
allowNull: false
},
minPayment: DataTypes.INTEGER,
dueDate: DataTypes.DATE
},
{
sequelize,
modelName: 'Account',
tableName: 'accounts'
}
)
return Account
}
Below is the User Model
'use strict'
const { Model } = require('sequelize')
module.exports = (sequelize, DataTypes) => {
class User extends Model {
/**
* Helper method for defining associations.
* This method is not a part of Sequelize lifecycle.
* The `models/index` file will call this method automatically.
*/
static associate(models) {
User.hasMany(models.Account, { foreignKey: 'accountId' })
}
}
User.init(
{
firstName: {
type: DataTypes.STRING,
allowNull: false
},
middleName: DataTypes.STRING,
lastName: {
type: DataTypes.STRING,
allowNull: false
},
email: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
validate: {
isEmail: true
}
},
password: {
type: DataTypes.STRING,
allowNull: false
}
},
{
sequelize,
modelName: 'User',
tableName: 'users'
}
)
return User
}
I've tried updating my Account model trying to remove the foreign key accountId
You made a mistake in the User's association:
You wrote:
User.hasMany(models.Account, { foreignKey: 'accountId' })
The correct one:
User.hasMany(models.Account, { foreignKey: 'userId' })

Sequelize Cyclic dependency found

I get this weird error when I was trying to sync my database:
Unhandled rejection Error: Cyclic dependency found. roles is dependent of itself.
Dependency chain: roles -> users => roles
I have this following junction table model called Permission
const Permission = db.define('permission', {
id: {
type: type.INTEGER,
primaryKey: true,
autoIncrement: true
},
role_id: {
type: type.INTEGER,
references: {
model: 'roles',
key: 'id',
}
},
resource_id: {
type: type.INTEGER,
references: {
model: 'resources',
key: 'id',
}
},
});
Why does this error happen? And how can I fix it? In my User model, User has one Role :
const User = db.define('user', {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
},
role_id : {
type: Sequelize.INTEGER,
references: {
model: 'roles',
key: 'id',
}
}
});
User.hasOne(Role)
Edit : Here's my Role model:
const Role = db.define('role', {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
},
name: Sequelize.STRING,
})
module.exports = Role
In sequelize docs it says:
The A.hasOne(B) association means that a One-To-One relationship
exists between A and B, with the foreign key being defined in the
target model (B)
Which means, User model shouldn't have a foreign key to Role model. Try changing your User model to:
const User = db.define('user', {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
}
});
User.hasOne(Role)
And then your Role model to:
const Role = db.define('role', {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
},
name: Sequelize.STRING,
user_id : {
type: Sequelize.INTEGER,
references: {
model: 'users',
key: 'id',
}
}
})
Role.belongsTo(User);
module.exports = Role

Why is Sequelize only returning one object using findAll()?

My goal is to be able to find all products by their brand name and model name. However, Sequelize is only returning one record out of many other similar records. If it does return more than one record, other records with identical attributes as the first record found will be null. For example, the first record in the array will have the attribute name: iPhone, the second record which has the exact same attribute will be shown as name: null when it should be name: iPhone.
In my database, I have the following tables:
Products, Brands, Models, and Suppliers. The Products table contains foreign keys such as brand_id, model_id, etc.. Brands, Models, and Suppliers have the attribute: id.
I have set the relationship up as the following:
Products.hasOne(Brands, { foreignKey: 'id' });
Products.hasOne(Models, { foreignKey: 'id' });
Products.hasOne(Suppliers, { foreignKey: 'id' });
Brands.belongsTo(Products);
Models.belongsTo(Products);
Suppliers.belongsTo(Products);
In my search function, I attempt to find all products by brand and model name that match my query.
const getSearch = (req, res) => {
const { query: { query } } = req;
Products.findAll({
where: Sequelize.where(Sequelize.fn('concat', Sequelize.col('Brand.name'), ' ', Sequelize.col('Model.name')), {
[Op.substring]: query
}),
include: [
{ model: Brands, attributes: ['name'] },
{ model: Models, attributes: ['name'] },
{ model: Suppliers, attributes: ['name'] },
],
attributes: ['id', 'price']
})
.then((data) => {
console.log(data);
res.send(data);
})
.catch((err) => (console.log(err)));
};
In my database, I have two product rows with the exact same data but different ids. When calling getSearch I expect to see two objects in the array as they have the same brand name and model name. Instead I see one.
Here's what my models look like:
Products
class Products extends Model {
static init(sequelize, DataTypes) {
return super.init(
{
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
url_id: {
type: DataTypes.INTEGER,
allowNull: true
},
brand_id: {
type: DataTypes.INTEGER,
allowNull: true
},
model_id: {
type: DataTypes.INTEGER,
allowNull: true
},
supplier_id: {
type: DataTypes.INTEGER,
allowNull: true
},
image: {
type: DataTypes.STRING,
allowNull: true
},
description: {
type: DataTypes.STRING,
allowNull: true
},
price: {
type: DataTypes.DOUBLE,
allowNull: true
}
},
{
modelName: 'Products',
timestamps: false,
sequelize
}
);
}
}
Models
class Models extends Model {
static init(sequelize, DataTypes) {
return super.init(
{
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
name: {
type: DataTypes.STRING,
allowNull: true
},
colour_id: {
type: DataTypes.INTEGER,
allowNull: true
},
storage_capacity_id: {
type: DataTypes.INTEGER,
allowNull: true
}
},
{
modelName: 'Models',
timestamps: false,
sequelize
}
);
}
}
Brands
class Brands extends Model {
static init(sequelize, DataTypes) {
return super.init(
{
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
name: {
type: DataTypes.STRING,
allowNull: true
}
},
{
modelName: 'Brands',
timestamps: false,
sequelize
}
);
}
}
Suppliers
class Suppliers extends Model {
static init(sequelize, DataTypes) {
return super.init(
{
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
name: {
type: DataTypes.STRING,
allowNull: true
}
},
{
modelName: 'Suppliers',
timestamps: false,
sequelize
}
);
}
}
What am I doing wrong here?
You have an error in associations. Just change hasOne to hasMany and you are done.

Sequelize assocation to same table

I have a table called documents that has a column called parentId which is a reference to another document record.
With my current code i'm getting the error
insert or update on table "documents" violates foreign key constraint "documents_parentId_fkey"
documents migration
'use strict'
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('documents', {
id: {
allowNull: false,
primaryKey: true,
type: Sequelize.UUID,
defaultValue: Sequelize.UUIDV4
},
parentId: {
allowNull: true,
type: Sequelize.UUID,
references: {
model: 'documents',
key: 'id'
}
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
lastUpdatedAt: {
allowNull: false,
type: Sequelize.DATE
},
lastUpdatedBy: {
allowNull: false,
type: Sequelize.UUID
}
})
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('documents')
}
}
document model
'use strict'
module.exports = (sequelize, DataTypes) => {
const document = sequelize.define('document', {
id: {
allowNull: false,
primaryKey: true,
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4
},
parentId: {
allowNull: true,
type: DataTypes.UUID,
references: {
model: 'documents',
key: 'id'
}
},
lastUpdatedBy: {
allowNull: false,
type: DataTypes.UUID
}
},
{
updatedAt: 'lastUpdatedAt'
})
document.associate = function (models) {
document.belongsTo(models.document, { foreignKey: 'parentId' })
}
return document
}
How do you properly do associations to the same table?
I have a self referencing table configured with the constraints: false setting.
MyModel.belongsTo(MyModel, {
as: 'parentMyModel',
foreignKey: 'parentId',
constraints: false,
});
Looks like the constraint is valid (and a good one). My payload that I was submitting had a parent uuid which didn't actually reference any document with that id.
So my code was right, the data I was submitting was wrong.

id: null when creating a new item in sequelize

When I try to create a new Conversation item Sequelize will return an object with id: null eventhough there is an valid id in the database. How can I get Sequelize to return the last inserted id to the newly created item?
Conversation.create({
type: 'private',
createdBy: 1,
}).then(conversation => {
reply(conversation);
});
Will return
{
"type": "conversations",
"id": null,
"createdBy": 1,
"created_at": "2016-03-18T01:47:48.000Z"
}
My code:
const Conversation = model.define('Conversation', {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
},
type: {
type: Sequelize.ENUM,
values: ['private', 'group'],
validate: {
isIn: ['private', 'group'],
},
},
createdBy: {
type: Sequelize.INTEGER,
field: 'created_by',
},
}, {
tableName: 'conversations',
timestamps: true,
createdAt: 'created_at',
updatedAt: false,
getterMethods: {
type: () => 'conversations',
},
});
const User = model.define('User', {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
},
firstName: {
type: Sequelize.STRING,
field: 'first_name',
allowNull: false,
},
lastName: {
type: Sequelize.STRING,
field: 'last_name',
allowNull: true,
},
email: {
type: Sequelize.STRING,
allowNull: false,
},
profileImg: {
type: Sequelize.STRING,
field: 'profile_img',
allowNull: false,
},
password: Sequelize.STRING,
}, {
tableName: 'users',
timestamps: true,
createdAt: 'created_at',
updatedAt: 'updated_at',
getterMethods: {
type: () => 'users',
},
});
Conversation.belongsToMany(User, {
foreignKey: 'conversation_id',
otherKey: 'user_id',
through: 'conversation_user',
timestamps: false,
});
User.belongsToMany(Conversation, {
as: 'conversations',
foreignKey: 'user_id',
otherKey: 'conversation_id',
through: 'conversation_user',
timestamps: false,
});
Yo need to put autoIncrement: true in id field:
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true,
}
Personally I would advice to skip the id column as sequalize does it automatically for you and works nicely.
hope it helps :)
Problem was my MySQL version (5.6 instead of 5.7), updated it and now I'm getting id's of the created items in the promise.
I'm not sure about how Sequelize is working with id field, I get null if I do instance.id, bug I can get the real value at DB if I do the following:
console.info(instance.id); // null
console.info(instance.get('id')); // 25 => Real ID
console.info(instance.getDataValue('id')); // 25 => Real ID
Something similar is happening with other fields like createdAt and updatedAt.
In order to get the real value at id field and other related fields, I added following logic to Model declaration:
class FooModel extends Model {
// ...
/**
* #inheritdoc
*/
public async save(options?: SaveOptions<TModelAttributes>): Promise<this> {
await super.save(options);
this.loadBaseData();
return this;
}
/**
* #inheritdoc
*/
public async reload(options?: FindOptions<TModelAttributes>): Promise<this> {
await super.reload(options);
this.loadBaseData();
return this;
}
private loadBaseData() {
this.id = this.getDataValue('id');
this.createdAt = this.getDataValue('createdAt');
this.updatedAt = this.getDataValue('updatedAt');
}
}
because if you only build without save it then:
instance.id // null
so you need:
instance.save()
instance.id // someNumber

Categories

Resources