首页 > 解决方案 > 使用 Sequelize 的关联:多对多关系

问题描述

我正在尝试构建一个使用 express、sequelize 和 mysql 的网络聊天应用程序。我的逻辑是一个用户属于很多聊天,一个聊天属于很多用户。我为这个关联创建了一个连接表,名为User_chat. 我最近才了解续集,并且仍在掌握逻辑。我有一个简单的获取请求:

router.get('/', withAuth, async (req, res) => {
  try {
    const userChatRooms = await Chat.findAll({
      include: [{ model: User_chat }]
    });

    console.log(userChatRooms);
    res.json(userChatRooms);
  } catch (e) {
    console.log(e);
    res.status(500).json(e);
  }

});

它总是返回以下错误:

EagerLoadingError [SequelizeEagerLoadingError]: User_chat is not associated to Chat!
    at Function._getIncludedAssociation (/Users/nem/Desktop/proj/chat/node_modules/sequelize/lib/model.js:710:13)
    at Function._validateIncludedElement (/Users/nem/Desktop/proj/chat/node_modules/sequelize/lib/model.js:614:53)
    at /Users/nem/Desktop/proj/chat/node_modules/sequelize/lib/model.js:509:37
    at Array.map (<anonymous>)
    at Function._validateIncludedElements (/Users/nem/Desktop/proj/chat/node_modules/sequelize/lib/model.js:504:39)
    at Function.findAll (/Users/nem/Desktop/proj/chat/node_modules/sequelize/lib/model.js:1723:12)
    at async /Users/nem/Desktop/proj/chat/controllers/api/chat-routes.js:8:27

我希望能够返回chat_name登录用户的。我想到的查询是:

select c.email, a.chat_name from chat a
left join user_chat b on a.chat_id = b.chat_id
left join user c on b.user_id = c.user_id
where c.user_id = 1;

我的关联表如下所示:

const { Chat } = require('./Chat');
const { User } = require('./User');
const { User_chat } = require('./User_chat');
const { Message } = require('./Message');

// Defining associations between the models

//TODO: Define between user and chat - many to many
//Junction table is user_chat
Chat.belongsToMany(User, { through: 'User_chat' });

//TODO: Define between chat and user - many to many
//Junction table is user_chat
User.belongsToMany(Chat, { through: 'User_chat' });

//TODO: Define between user and message - one to many
User.hasMany(Message, {
  foreignKey: 'sender_id',
  onDelete: 'CASCADE'
});

//TODO: Define between chat and message - one to many
Chat.hasMany(Message, {
  foreignKey: 'chat_id',
  onDelete: 'CASCADE'
});

//TODO: Define between message and chat - one to one
Message.belongsTo(Chat, {
  foreignKey: 'chat_id'
});

//TODO: Define between message and user - one to one
Message.belongsTo(User, {
  foreignKey: 'sender_id'
});

module.exports = {
  Chat,
  User,
  User_chat,
  Message
};

模型设计


const { Model, DataTypes } = require('sequelize');
const bcrypt = require('bcryptjs');
const sequelize = require('../config/connection.js');

class User extends Model {
  checkPassword(userLoginPassword) {
    return bcrypt.compareSync(userLoginPassword, this.password);
  }
}

User.init(
  //TODO: Define the model for users
  {
    user_id: {
      type: DataTypes.INTEGER,
      autoIncrement: true,
      primaryKey: true
    },
    username: {
      type: DataTypes.STRING(30),
      allowNull: false,
      unique: true
    },
    email: {
      type: DataTypes.STRING(255),
      allowNull: false,
      unique: true
    },
    password: {
      type: DataTypes.STRING(255),
      allowNull: false
    }
  },
  {
    hooks: {
      async beforeCreate(newUserData) {
        newUserData.password = await bcrypt.hash(newUserData.password, 10);
        return newUserData;
      }
      // async beforeUpdate(updatedUserData) {
      //   updatedUserData.password = await bcrypt.hash(
      //     updatedUserData.password,
      //     10
      //   );
      //   return updatedUserData;
      // }
    },
    sequelize,
    timestamps: false,
    freezeTableName: true,
    underscored: true
  }
);

module.exports = { User };
const { Model, DataTypes } = require('sequelize');

const sequelize = require('../config/connection.js');

class Chat extends Model {}

Chat.init(
  //TODO: Define the model for chats
  {
    chat_id: {
      type: DataTypes.INTEGER,
      autoIncrement: true,
      primaryKey: true
    },
    chat_name: {
      type: DataTypes.STRING(255),
      unique: true,
      allowNull: false
    }
  },
  {
    sequelize,
    timestamps: false,
    freezeTableName: true,
    underscored: true
  }
);

module.exports = { Chat };
const { Model, DataTypes } = require('sequelize');

const sequelize = require('../config/connection.js');

class User_chat extends Model {}

User_chat.init(
  //Junction table for the user and chat id since it will be many to many.
  {
    user_id: {
      type: DataTypes.INTEGER
      references: {
        model: 'User',
        key: 'user_id'
      }
    },
    chat_id: {
      type: DataTypes.INTEGER
      references: {
      model: 'Chat',
      key: 'chat_id'
      }
    },
    is_owner: {
      type: DataTypes.BOOLEAN
    }
  },
  {
    sequelize,
    timestamps: false,
    freezeTableName: true,
    underscored: true
  }
);

module.exports = { User_chat };
const { Model, DataTypes, literal } = require('sequelize');

const sequelize = require('../config/connection.js');

class Message extends Model {}

Message.init(
  //TODO: Define the model for message
  {
    message_id: {
      type: DataTypes.INTEGER,
      primaryKey: true,
      autoIncrement: true
    },
    message_content: {
      type: DataTypes.STRING(255),
      allowNull: false
    },
    chat_id: {
      type: DataTypes.INTEGER,
      references: {
        model: 'chat',
        key: 'chat_id'
      }
    },
    sender_id: {
      type: DataTypes.INTEGER,
      references: {
        model: 'user',
        key: 'user_id'
      }
    }
    // created_at: {
    //   type: 'TIMESTAMP',
    //   defaultValue: literal('CURRENT_TIMESTAMP'),
    //   allowNull: false
    // }
  },
  {
    sequelize,
    timestamps: false,
    freezeTableName: true,
    underscored: true
  }
);

module.exports = { Message };

标签: javascriptmysqlnode.jsexpresssequelize.js

解决方案


如果您希望获得所有用户的聊天记录,请指明User模型,include而不是连接模型。此外,与用户开始查询更容易,因为您希望为其添加条件:

const userWithChats = await User.findAll({
      where: {
        user_id: 1
      },
      attributes: ['email'],
      include: [{
        model: Chat,
        attributes: ['chat_name']
      }]
    });
const chats = userWithChats.Chats

推荐阅读