Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sequelize cyclic dependency

This is my User model

'use strict';
var bcrypt = require('bcrypt');

module.exports = function(sequelize, DataTypes) {
  var User = sequelize.define('User', {
    email: {
      type: DataTypes.STRING,
      validate: {
        isEmail: true,
        notEmpty: true,
        notNull: false
      },
      unique: true
    },

    password: DataTypes.STRING,
    name: DataTypes.STRING,

    username: {
      type: DataTypes.STRING,
      unique: true
    },

    admin: DataTypes.BOOLEAN,

    googleId: DataTypes.BOOLEAN

  }, {
    classMethods: {
      associate: function(models) {
        User.hasMany(models.Award);
        User.hasMany(models.Media);
        User.hasMany(models.Comment);
        User.hasMany(models.Like);
        User.hasMany(models.CheckIn);
      }
    }
  });


  return User;
};

and this is my Media model:

'use strict';

module.exports = function(sequelize, DataTypes) {
  var Media = sequelize.define('Media', {

    type: DataTypes.ENUM('photo', 'video'),
    description: DataTypes.STRING,

    url: DataTypes.STRING,

    gps: DataTypes.GEOMETRY('POINT')

  }, {
    classMethods: {
      associate: function(models) {
        //Media.belongsTo(models.Event);
        //Media.belongsTo(models.User);
        Media.hasMany(models.Comment);
        Media.hasMany(models.Like);
      }
    }
  });


  return Media;
};

And I'm getting this error:

Unhandled rejection Error: Cyclic dependency found. Users is dependent of itself.
Dependency chain: Awards -> Users -> Media => Users

Previously I had a cyclic dependency and it's now removed but sequelize still throws this error. Why is this happening?

If I remove the User.hasMany(models.Media) association the error will disappear. But why is it still happening when the Media model has no reference to the User model?

like image 390
andrepcg Avatar asked Jan 31 '16 16:01

andrepcg


People also ask

How do you solve cyclic dependency?

A cyclic dependency is an indication of a design or modeling problem in your software. Although you can construct your object graph by using property injection, you will ignore the root cause and add another problem: property injection causes Temporal Coupling. Instead, the solution is to look at the design closely.

How do I add a foreign key constraint in Sequelize?

Sequelize can add a foreign key constraint to the desired SQL table by using one of the following association methods: hasOne() belongsTo()

What are constraints in Sequelize?

Constraints are the rules that we define on the level of SQL. For example, if one attribute is supposed to be unique then this will be handled on the level of SQL. If a constraint check fails then Sequelize will forward the error message sent by the database.

How do I add a foreign key in migration Sequelize?

To create an attribute with a foreign key relationship, use the "references" and "referencesKey" fields: For example, the following would create a users table, and a user_emails table which references the users table. queryInterface. createTable('users', { id: { type: Sequelize.


2 Answers

Settings constraints: false will work, but will not create the foreign key in your DB.

If all your db access is done from sequelize, that can be an acceptable solution. But if you access your DB in several ways, that becomes complicated to handle. E.g: Hasura does not create relationships between models.

The real issue is that sequelize is not smart enough to create the table in 2 steps. Other ORMs handle that by first creating the tableA without the foreign key, create the tableB with foreignKey to tableA, and alter TableA to create foreign key to tableB.

So the solution is to add constraints: false to your User.hasMany(models.Media) then create then run foreign key constrain.

    const addMediaUserForeignKey = queryInterface.addConstraint(
      'Media', {
        type: 'foreign key',
        onUpdate: 'CASCADE',
        onDelete: 'CASCADE',
        references: {
          table: 'User',
          field: 'id',
        },
        fields: [ 'userId' ]
      }
    ).catch((e) => {})
like image 110
Marc Simon Avatar answered Sep 29 '22 18:09

Marc Simon


Consider using hasMany as follows:

User.hasMany(models.Award, {as: 'ifYouWantAlias', constraints: false, allowNull:true, defaultValue:null});

note that you don't need the following part but it makes it clearer in my opinion.

allowNull:true, defaultValue:null

It is explained well in here: http://docs.sequelizejs.com/en/latest/api/associations/

like image 43
ozgeneral Avatar answered Sep 29 '22 16:09

ozgeneral