sequelize: belongsToMany Association on the same model with a different foreign key and alias fails

What you are doing?

I have defined my association on the User model as follows

    User.belongsToMany(User, {
      through: models.Following,
      as: 'Follower',
      foreignKey: 'following_id',
    });

    User.belongsToMany(User, {
      through: models.Following,
      as: 'Following',
      foreignKey: 'follower_id',
    });

What do you expect to happen?

I did this so that I could then call user.GetFollower() and user.GetFollowing() functions. This worked fine with sequelize v3

What is actually happening?

With sequelize v4, I now get the error SequelizeAssociationError: You have used the alias Following in two separate associations. Aliased associations must have unique aliases.

If I remove the second User.belongsToMany, it solves the problem but I am no longer able to use user.getFollowing() which returns as undefined.

Dialect: mysql Database version: 5.7 Sequelize version: 4.8

P.S. I did check https://github.com/sequelize/sequelize/issues/7775. But it is not applicable to my case. I do not have the Model.associate(models) being called multiple times. I have it called only once - in the index.js file that sequelize-cli generated.

About this issue

  • Original URL
  • State: open
  • Created 7 years ago
  • Reactions: 12
  • Comments: 25 (2 by maintainers)

Most upvoted comments

That… that is not good. The longer I use it, the more I think I’m just going to write my queries as needed, and start moving away from Sequelize as a data object layer. Which stinks; in its basic form, a couple versions old, it was a great way to get basic, simple-data-model with basic relations working quickly and clearly. There’s just too much finicky tweaking the last year or two for my liking. 😦

Chris Hornberger chris.hornberger@gmail.com

On Thu, Jul 26, 2018 at 9:38 AM, Bogdan Polovko notifications@github.com wrote:

I’m trying to update to Sequelize 4. In my case I have two models using the same alias. That’s how front-end expects it to be:

Clinic.belongsTo(Location, { as: ‘location’, foreignKey: ‘locationId’ });Location.hasOne(Clinic, { as: ‘location’, foreignKey: ‘locationId’ }); Company.belongsTo(Location, { as: ‘location’, foreignKey: ‘locationId’ });Location.hasOne(Company, { as: ‘location’, foreignKey: ‘locationId’ });

Why should this throw an error, what’s wrong with using two same aliases on two different models? Is there a way to disable it? Otherwise all our front-end applications will have to be updated, which is insane amount of work.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/sequelize/sequelize/issues/8263#issuecomment-408100247, or mute the thread https://github.com/notifications/unsubscribe-auth/AHJ5ebUajwAF7LgN1bOZnTC7woS3_VWSks5uKcZPgaJpZM4POm7F .

In version 4.37.7, I ran into this with the following structure:

Prairie.hasMany(PrairieFlowerSpecies, { foreignKey: 'prairie_id', sourceKey: 'prairie_id' });
Prairie.belongsToMany(FlowerSpecies, {
  through: PrairieFlowerSpecies,
  as: 'flower_species',
  foreignKey: 'flower_species_id',
  otherKey: 'prairie_id',
});

I took a look at the source code and it looks like the lib is temporarily? defining a relationship between Prairie and PrairieFlowerSpecies in the process of adding the belongsToMany relationship, even though one already exists. Rearranging it eliminated the error:

Prairie.belongsToMany(FlowerSpecies, {
  through: PrairieFlowerSpecies,
  as: 'flower_species',
  foreignKey: 'flower_species_id',
  otherKey: 'prairie_id',
});
Prairie.hasMany(PrairieFlowerSpecies, { foreignKey: 'prairie_id', sourceKey: 'prairie_id' });

This doesn’t look like it would fix OP’s problem but it’s weird and I hope it helps somebody out. Thanks for this thread folks.

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. If this is still an issue, just leave a comment 🙂

I’ve got the same issue, it really doesn’t make any sense, and now I’m stuck using the term ‘Availabilities’ on my model which is a terrible name.

This might not be your problem, but try setting the alias singular and plural form of your association: User.belongsToMany(User, { as: { singular: 'follows', plural: 'following; }})

Since Sequelize uses the inflection library to convert aliases to, in this case, their singular form, read about association naming strategy

And the singular form of Following is Following this might be your problem.

inflection.singularize( 'following');
"following"

@chornbe to sequelize maintainers defence, they do a good use of semantic versioning, not sure what’s your point.

I have reproduced the issue with the following super-minimal code (by completing OP’s code):

const Sequelize = require("sequelize");
const sequelize = new Sequelize("sqlite::memory:");

const User = sequelize.define('User', {});
const Following = sequelize.define('Following', {});

User.belongsToMany(User, {
    through: Following,
    as: 'Follower',
    foreignKey: 'following_id',
});
User.belongsToMany(User, {
    through: Following,
    as: 'Following',
    foreignKey: 'follower_id',
});

Result:

SequelizeAssociationError: You have used the alias Following in two separate associations. Aliased associations must have unique aliases.

@papb I just changed the name of alias & the issue was gone. I am just guessing that it can be due to the there already being model’s name - Following and therefore sequelize might be wanting unique(all model names + all association alias names) for not having problems when guessing in include. 💡 Which in brief means give unique names wherever you are providing names on your own.

Below code does not give error.

const Sequelize = require("sequelize");
const sequelize = new Sequelize("sqlite::memory:");

const User = sequelize.define('User', {});
const Following = sequelize.define('Following', {}); // <--- 'Following' already used here

User.belongsToMany(User, {
    through: Following,
    as: 'FollowerUsers',  // <--- unique alias name
    foreignKey: 'following_id',
});
User.belongsToMany(User, {
    through: Following,
    as: 'FollowingUsers', // <--- unique alias name
    foreignKey: 'follower_id',
});

I have reproduced the issue with the following super-minimal code (by completing OP’s code):

const Sequelize = require("sequelize");
const sequelize = new Sequelize("sqlite::memory:");

const User = sequelize.define('User', {});
const Following = sequelize.define('Following', {});

User.belongsToMany(User, {
    through: Following,
    as: 'Follower',
    foreignKey: 'following_id',
});
User.belongsToMany(User, {
    through: Following,
    as: 'Following',
    foreignKey: 'follower_id',
});

Result:

SequelizeAssociationError: You have used the alias Following in two separate associations. Aliased associations must have unique aliases.

Seems that association declarations are completely changed now, and instance and class methods have been removed in 4.x as I understand it. I guess backward compatibility is a thing of the past.

one of my biggest problems using ES6 to declare the associations it was because the static method is never called by himself so i has to call it before start doing requests

const schema = new ApolloServer({
  typeDefs,
  resolvers,
  playground: {
    endpoint: "/graphql"
  },
  context: {
    CostPostgreSql: CostPostgreSql.init(sequelize),
    PricePostgreSql: PricePostgreSql.init(sequelize),
    ProductPostgreSql: ProductPostgreSql.init(sequelize)
  }
});
ProductPostgreSql.associate(sequelize);
PricePostgreSql.associate(sequelize);
CostPostgreSql.associate(sequelize);

const app = express();
schema.applyMiddleware({ app });

app.listen(3000, () => {
  console.log("Live on http://localhost:3000/graphql");
});

i declare my relations as follows in the products model, the id of each product is inside of each cost and price element so i use belongsTo when the id is going to be on the source and hasMany when the id is going to be on the target

static associate({ models }) {
    this.belongsTo(models.Unit, { foreignKey: "id" });
    this.belongsTo(models.Provider, { foreignKey: "id" });
    this.hasMany(models.Cost, { foreignKey: "product" });
    this.hasMany(models.Price, { foreignKey: "product" });
  }

and on the costs and prices models i declare something like

  static associate({ models }) {
    this.belongsTo(models.Product, { foreignKey: "id" });
  }

to the moment of make a query i did something like

import { CostPostgreSql } from "../models";
import { PricePostgreSql } from "../models";
db
    .findAll({
      where: { id },
      include: [
        {
          model: CostPostgreSql
        },
        {
          model: PricePostgreSql
        }
      ]
    })
    .then(response => {
      console.log("Exito");
      console.log(JSON.stringify(response));
      return response;
    })
    .catch(err => {
      console.log("fracaso");
      console.log(err);
      return err;
    });

And everything is going well

@chornbe @kytwb @callmekatootie Please what is the state of this issue? Where you able to resolve this in anyway? I am having the same issue.

Song.belongsToMany(models.Term, { through: models.SongCategory, as: 'SongCategory', foreignKey: 'songId' })
Term.belongsToMany(models.Song, {through: models.SongCategory, as: 'Category', foreignKey: 'categoryId'})

I recently encountered a similar problem with the call as follows:

    Transaction.belongsTo(TransactionMode, {
      foreignKey: "transaction_mode_id",
      as: 'transaction_mode'
    });
    Transaction.belongsTo(Account, {
      foreignKey: "initiator_account_id",
      as: 'transaction_initiator'
    });
    Transaction.belongsTo(Account, {
      foreignKey: "counterparty_account_id",
      as: 'transaction_counterparty'
    });
    Transaction.belongsTo(Account, {
      foreignKey: "shipping_fee_paid_by_account_id",
      as: 'shipping_fee_payer'
    });

    Transaction.belongsTo(Account, {
      foreignKey: "escrow_fee_paid_by_account_id",
      as: 'escrow_fee_payer'
    });

    Transaction.belongsTo(TransactionStatus, {
      foreignKey: "finished_transaction_status_id",
      as: 'finished_transaction_status'
    });

    Transaction.belongsTo(ShippingMethod, {
      foreignKey: "shipping_method_id",
      as: 'transaction_shipping_method'
    });

    Transaction.belongsTo(ShippingMethod, {
      foreignKey: "inspection_items_refused_shipping_method_id",
      as: 'return_shipping_method'
    });

    const transaction = await Transaction.findOne({
      where: {
        uuid: transactionUuid
      },
      include: ['transaction_mode', 'transaction_initiator', 'finished_transaction_status', 'transaction_shipping_method',
        'transaction_counterparty', 'shipping_fee_payer',
        'return_shipping_method', 'escrow_fee_payer']
    });

The call produces the following error: ‘You have used the alias transaction_mode in two separate associations. Aliased associations must have unique aliases.’ After digging into the model at https://github.com/nadrane/sequelize/blob/c41f51651b82d7c3f1e70c6a23b59fa27c0b1034/lib/model.js#L2792C28-L2792C40, I found a simple way to resolve the error: by resetting the associations for the given source, as shown below:

Transaction.associations = new Array;

The full, working call is:

    Transaction.associations = new Array;
    Transaction.belongsTo(TransactionMode, {
      foreignKey: "transaction_mode_id",
      as: 'transaction_mode'
    });
    Transaction.belongsTo(Account, {
      foreignKey: "initiator_account_id",
      as: 'transaction_initiator'
    });
    Transaction.belongsTo(Account, {
      foreignKey: "counterparty_account_id",
      as: 'transaction_counterparty'
    });
    Transaction.belongsTo(Account, {
      foreignKey: "shipping_fee_paid_by_account_id",
      as: 'shipping_fee_payer'
    });

    Transaction.belongsTo(Account, {
      foreignKey: "escrow_fee_paid_by_account_id",
      as: 'escrow_fee_payer'
    });

    Transaction.belongsTo(TransactionStatus, {
      foreignKey: "finished_transaction_status_id",
      as: 'finished_transaction_status'
    });

    Transaction.belongsTo(ShippingMethod, {
      foreignKey: "shipping_method_id",
      as: 'transaction_shipping_method'
    });

    Transaction.belongsTo(ShippingMethod, {
      foreignKey: "inspection_items_refused_shipping_method_id",
      as: 'return_shipping_method'
    });

    const transaction = await Transaction.findOne({
      where: {
        uuid: transactionUuid
      },
      include: ['transaction_mode', 'transaction_initiator', 'finished_transaction_status', 'transaction_shipping_method',
        'transaction_counterparty', 'shipping_fee_payer',
        'return_shipping_method', 'escrow_fee_payer']
    });

Enjoy! 😃

@smithaitufe iirc, I ended up not using Following or rather name of the model itself for the through property and instead using Followings or something not same as the model name which then worked.

I have the same problems with the following association:

Users.belongsToMany(models.Places, { through: 'Checkins', as: 'Checkins' })
Places.belongsToMany(models.Users, { through: 'Checkins', as: 'Checkins' })

I tried removing the alias on one-or-the-other and it still fails.
I ended up removing the aliases but it makes the code readability confusing.
Subscribing.