Search code examples
sequelize.jsforestadmin

Sequelize - Can i associate a model based on another model relation?


Good morning, i have been trying to solve a problem but i do not have enough experience with sequelize to know if there is a solution or not so here is my problem:

I have one model named Order, another model named Service and 8 other models named "orderAttributeA", "orderAttributeB", "orderAttributeC" ....

my order model work as below:

module.exports = (sequelize, DataTypes) => {
const {Sequelize} = sequelize;
// Learn more here: https://docs.forestadmin.com/documentation/v/v6/reference-guide/models/enrich-your-models#declaring-a-new-field-in-a-model
const Order = sequelize.define('order', {
    id: {
        type: DataTypes.BIGINT,
        autoIncrement: true,
        primaryKey: true
    },
    userId: {
        type: DataTypes.BIGINT,
        allowNull: false,
    },
    companyId: {
        type: DataTypes.BIGINT,
        allowNull: false,
    },
    serviceId: {
        type: DataTypes.BIGINT,
        allowNull: false,
    },
    providerId: {
        type: DataTypes.BIGINT,
        allowNull: true,
        defaultValue: null
    },
    status: {
        type: DataTypes.INTEGER,
        allowNull: false,
    },
    userComment: {
        type: DataTypes.STRING,
        allowNull: true,
        defaultValue: null
    },
    amount: {
        type: DataTypes.INTEGER,
        defaultValue: 0,
        allowNull: false,
    },
    deliveryDate: {
        type: DataTypes.DATE,
        allowNull: true,
        defaultValue: null
    },
    createdAt: {
        type: DataTypes.DATE,
        defaultValue: Sequelize.literal('CURRENT_TIMESTAMP'),
    },
    updatedAt: {
        type: DataTypes.DATE,
        defaultValue: Sequelize.literal('CURRENT_TIMESTAMP'),
        onUpdate: Sequelize.literal('CURRENT_TIMESTAMP')
    },
}, {
    tableName: 'Order',
});

Order.associate = async (models) => {
    Order.belongsTo(models.user, {
        foreignKey: 'userId',
    });

    Order.hasMany(models.orderDetail, {
        foreignKey: 'orderId',
        as: 'details',
    });

    Order.belongsTo(models.service, {
        foreignKey: 'serviceId',
        as: 'service',
    });

    Order.hasOne(models.orderPayment, {
        foreignKey: 'orderId',
        as: 'payment',
    });
};

return Order;

};

my service model :

module.exports = (sequelize, DataTypes) => {
  const { Sequelize } = sequelize;
  const Service = sequelize.define('service', {
    name: {
      type: DataTypes.STRING,
      allowNull: false,
    },
    className: {
      type: DataTypes.STRING,
      allowNull: false,
    },
    categoryId: {
      type: DataTypes.INTEGER,
      allowNull: false,
    },
    externalLink: {
      type: DataTypes.STRING,
    },
    createdAt: {
      type: DataTypes.DATE,
      defaultValue: Sequelize.literal('CURRENT_TIMESTAMP'),
    },
    updatedAt: {
      type: DataTypes.DATE,
      defaultValue: Sequelize.literal('CURRENT_TIMESTAMP'),
      onUpdate: Sequelize.literal('CURRENT_TIMESTAMP')
    },
  }, {
    tableName: 'Service',
  });

  Service.associate = (models) => {
  };

  return Service;
};

one of my orderAttributeXXX model :

module.exports = (sequelize, DataTypes) => {
  const { Sequelize } = sequelize;
  const OrderAttributeB = sequelize.define('orderAttributeB', {
    orderId: {
      type: DataTypes.BIGINT,
      primaryKey: true,
      allowNull: false,
    },
    firstName: {
      type: DataTypes.STRING,
      allowNull: false,
    },
    lastName: {
      type: DataTypes.STRING,
      allowNull: false,
    },
    createdAt: {
      type: DataTypes.DATE,
      defaultValue: Sequelize.literal('CURRENT_TIMESTAMP'),
    },
    updatedAt: {
      type: DataTypes.DATE,
      defaultValue: Sequelize.literal('CURRENT_TIMESTAMP'),
      onUpdate: Sequelize.literal('CURRENT_TIMESTAMP')
    },
  }, {
    tableName: 'OrderAttributeB',
  });

  OrderAttributeB.associate = (models) => {
  };

  return OrderAttributeB;
};

What i am trying to do is to select the corresponding model (orderAttributeXXX) based on the on the className of my service so the relation looks like this : Order.serviceId -> service where the id = order.serviceId -> get the service className column -> find the model based on the service className column BUT There is no link between the model "service" and any of the "orderAttributeXXX" models. Is it possible to achieve something like this in sequelize ?:

// i would like to select the corresponding model (orderAttribute) based on the column className of my model service
    Order.belongsTo(models.service.className, {
        foreignKey: 'orderId',
        as: 'attribute',
    });

Thanks in advance


Solution

  • This is not something (dynamic reference field) supported by forestadmin out of the box.

    A hacky solution for this kind of issue would be with a smart collection + smart field

    For example, you could defined a smart collection OrderAttributes defined like so

    collection('OrderAttributes', {
      fields: [{ 
        field: 'id', // This is mandatory
        type: 'String' 
      }, { 
        field: 'orderAttributeA', 
        type: 'String', 
      }, { 
        field: 'orderAttributeA', 
        type: 'orderAttributeB',
      }, ...]
    });
    

    Then override routes for /forest/order-attributes, /forest/order, etc. to handle a smart relationship between Order & OrderAttributes.