Sequelize Cyclic dependency found - javascript

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

Related

Sequlize How to make bulkCreate with associated table and not create new values if theay already exist in table

I Have 2 models. Actors And Movies, they have BelongsToMany Asscociation
const Movie = sequelize.define(
MOVIES,
{
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
},
title: {
type: DataTypes.STRING,
allowNull: false,
},
year: {
type: DataTypes.NUMBER,
allowNull: false,
},
format: {
type: DataTypes.ENUM,
values: [VHS, DVD, BLU_RAY],
allowNull: false,
},
},
{
indexes: [
{
unique: true,
fields: ['title'],
},
],
}
);
const Actor = sequelize.define(
ACTORS,
{
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
},
name: {
type: DataTypes.STRING,
allowNull: false,
},
},
{
indexes: [
{
unique: true,
fields: ['name'],
},
],
}
);
and this logic:
const moviesData = req.files.movies.data.toString();
const newMovies = movieHelper.formatArrayOfMovieObjects(moviesData);
const movies = await Movie.bulkCreate(newMovies, {
include: {
model: Actor,
},
updateOnDuplicate: ['title'],
});
res.json(movies).status(200);
How to make to not create new records if movie.title exist in table
I tried updateOnDuplicate param but it give me this error: [Error: SQLITE_CONSTRAINT: FOREIGN KEY constraint failed
If your SQLite version supports unique constraints/indexes then you can create one indicating the title field and this way the option updateOnDuplicate should work well.

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.

sequelize - Cannot add foreign key constraint

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'
}
},

Nodejs use JOIN for two tables on sequelize

I'm trying to implementing this mysql command on sequelize, but as far as i'm newbie to use this library i can't implementing that
i want to make this sql command:
SELECT * FROM users
join users_contacts_lists on users_contacts_lists.mobile_number = users.mobile_number
WHERE users_contacts_lists.user_id = 1
My models to create database schema:
'use strict';
var config = require('../config');
var User = config.sequelize.define('users', {
id: {
type: config.Sequelize.INTEGER,
autoIncrement: true,
primaryKey: true
},
password: {
type: config.Sequelize.STRING
},
username: {
type: config.Sequelize.STRING
},
mobileNumber: {
type: config.Sequelize.STRING,
field: 'mobile_number'
},
status: {
type: config.Sequelize.STRING
},
}, {freezeTableName: true});
var UsersContactsLists = config.sequelize.define('users_contacts_lists', {
id: {
type: config.Sequelize.INTEGER,
autoIncrement: true,
primaryKey: true
},
userId: {
type: config.Sequelize.INTEGER,
field: 'user_id'
},
mobileNumber: {
type: config.Sequelize.STRING,
field: 'mobile_number', defaultValue: 0
}
}, {freezeTableName: true});
UsersContactsLists.belongsTo(ChannelsTypes, {foreignKey: 'user_id'});
User.hasMany(Channels, {foreignKey: 'id'});
User.sync();
UsersContactsLists.sync();
module.exports =
{
users: User,
usersContactsLists: UsersContactsLists
};
how can i resolve this problem? Thanks in advance
You can define the target key and the foreign key both in a relation like this:
User.belongsTo(UsersContactsLists, {targetKey:'mobileNumber',foreignKey: 'mobileNumber'});
And then you can use this:
User.findAll({
include: [{
model: UsersContactsLists,
where: {
userId: 1
}
}]
})
Fiddled around a bit, does this statement do what you want?
User.findAll({
include: [{
model: UsersContactsLists,
where: {
userId: 1
}
}]

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