【问题标题】:Sequelize: How to make relationship between 3 tablesSequelize:如何在 3 个表之间建立关系
【发布时间】:2019-01-03 03:20:25
【问题描述】:

如果您有以下实体:用户、角色、组织。 您想要设置关系,以便每个用户都有一个组织角色。

简单来说,每个用户可以属于多个组织,并且用户在每个组织中都有特定的角色。

您将如何使用 Sequelize 进行建模?

我尝试创建一个名为 organization_users 的联结表,然后在该表中添加一个 organizationUsers.belongsTo(role); 从我读过 Sequelize 不支持联结表上的关联,因此该解决方案不起作用。

问候, 埃米尔

【问题讨论】:

    标签: node.js sequelize.js


    【解决方案1】:

    Sequelize 确实支持连接表上的关联。您应该查看“通过”选项here >

    例子:

    const Asso_Organization_User = sequelize.define('Asso_Organization_User', {
        id: DataTypes.STRING,
        userId: DataTypes.STRING,
        organizationId: DataTypes.STRING
    });
    User.Organizations = User.belongsToMany(Organization, {
        through: Asso_Organization_User,
        foreignKey: 'userId',
        otherKey: 'organizationId',
        as: 'organizations'
    })
    

    但是您的情况有点特殊,我看不到使用 sequelize 获取用户、其所有组织以及他在同一查询中对每个组织的角色的方法。 看起来还没有解决方案,基于这个问题:https://github.com/sequelize/sequelize/issues/6671

    也许您可以通过两个查询来完成:获取所有组织和所有角色,然后将它们与代码合并。

    其实我看到了另一种解决方案:

    const Asso_Organization_User = sequelize.define('Asso_Organization_User', {
        id: DataTypes.STRING,
        userId: DataTypes.STRING,
        organizationId: DataTypes.STRING
    });
    User.Organizations = User.belongsToMany(Organization, {
        through: Asso_Organization_User,
        foreignKey: 'userId',
        otherKey: 'organizationId',
        as: 'organizations'
    })
    

    模型如下所示:

    User: id
    Role: id, userId, organizationId
    Organizations: id
    Asso_Organization_User: id, userId, organizationId
    

    然后:

    User.Organizations = User.belongsToMany(Organization, {
        through: Asso_Organization_User
    })
    Organization.Roles = Organization.haMany(Role, {
        foreignKey: 'organizationId'
    })
    

    然后你应该可以查询了:

       User.findAll({
            include: [ {
                model: Organization,
                include: {
                    model: Role  
                }
            } ]
            where: {
                'role.userId': Sequelize.col("User.id")
            }
        });
    

    我不完全确定确切的语法,但是将 Sequelize.col 与 hasMany 结合起来应该可以工作。但请注意,如果您不添加此 where 子句,这将返回每个组织的所有角色(对于在该组织中具有角色的每个用户)。

    【讨论】:

    • 感谢 Philippe 抽出宝贵时间写这篇文章,我会在接下来的 2 天内对其进行测试并回复您。
    • 给问题增加了一个复杂性:p...我有权限和角色的概念,角色只是权限的组合。所以角色有一个属于许多有权限的人。关于这一点,您将如何处理 Role: id, userId, organizationId 模型?
    • 它不会改变角色模型,您的权限表只会有一个指向角色的 roleId 列。
    • 我对续集还很陌生,所以对一些问题表示歉意。如果权限和角色具有多对多关系,我将如何建立:“您的权限表将只有一个指向角色的 roleId 列”。目前我有一个 role_permissions 表,它有权限和角色 ID 外键。
    • 在多对多的情况下,您确实需要一个 role_permission 表来建立两者之间的关联。为此,我建议查看 sequelize doc,尤其是“多对多”关联部分,寻找“通过”选项:docs.sequelizejs.com/manual/tutorial/…如果您在这方面需要进一步帮助,请随时提出新问题。跨度>
    【解决方案2】:

    根据您从这 3 个表中获取数据的要求可能会有所不同 考虑这个例子: 表:登录、用户配置文件、农民 要求:农民有一个用户资料,用户资料有一个登录名。 除了这些表格,我们还可以将用户注册为 Farmer。

    --------------------------------------------------------------------------
    farmer.js
    
    module.exports = (Sequelize, DataTypes) => {
      const Farmer = Sequelize.define("farmer", { /*attributes*/});
      return Farmer;
    };
    
    --------------------------------------------------------------------------
    login.js
    
    module.exports = (Sequelize, DataTypes) => {
      const Login = Sequelize.define("login", {*attributes*/});
    
      return Login;
    };
    --------------------------------------------------------------------------
    userProfile.js
    
    module.exports = (Sequelize, DataTypes) => {
      const UserProfile = Sequelize.define("userProfile", {*attributes*/});
    
      return UserProfile;
    };
    
    --------------------------------------------------------------------------
    index.js
    
    const dbConfig = require("../config/dbConfig"); // your config file
    const { Sequelize, DataTypes } = require("sequelize");
    
    //object initilize. (pass parameter to constructor)
    const sequelize = new Sequelize(dbConfig.DB, dbConfig.USER, dbConfig.PASSWORD, {
      host: dbConfig.HOST,
      dialect: dbConfig.dialect,
      operatorsAliases: false, //hide errors
      pool: {
        max: dbConfig.pool.max,
        min: dbConfig.pool.min,
        acquire: dbConfig.pool.acquire,
        idle: dbConfig.pool.idle,
      },
    });
    
    sequelize
      .authenticate()
      .then(() => {
        console.log("DB connected!");
      })
      .catch((err) => {
        console.log("Error " + err);
      });
    
    const db = {}; // Empty object
    
    db.Sequelize = Sequelize;
    db.sequelize = sequelize;
    
    db.login = require("./login.js")(sequelize, DataTypes);
    db.userProfile = require("./userProfile.js")(sequelize, DataTypes);
    db.farmer = require("./farmer.js")(sequelize, DataTypes);
    
    //relations
    db.farmer.userProfile = db.farmer.belongsTo(db.userProfile);
    
    db.userProfile.login = db.userProfile.belongsTo(db.login);
    
    db.sequelize
      .sync({ force: false }) //force :true - drop all tables before start
      .then(() => {
        console.log("yes-sync done!");
      });
    
    module.exports = db;
    
    //Declare follwing things in a separate location (may be controllers__.js
    -----------INSERT DATA (CREATE)--------------------------
    
    const saved = await Farmer.create(
        {
          supplierCode: "SUP0001",
          userProfile: {
            firstName: "ssss",
            middleName: "ssss",
            lastName: "ssss",
            address: "ssss",
            login: {
              name: "ssss",
              email: "ssss",
              password: "ssss",
              role: "ssss",
              lastLogin: null,
              avatar: "ssss",
              status: "ssss",
            },
          },
        },
        {
          include: [
            {
              association: Farmer.userProfile,
              include: [Login],
            },
          ],
        }
      );
    

    观察 Farmer.create 调用中包含选项的用法。这对于 Sequelize 了解您要与关联一起创建的内容是必要的。

    注意:这里,我们的用户模型叫做farmer,带有一个小写的f——这意味着对象中的属性也应该是farmer。如果 sequelize.define 的名称是 Farmer,则对象中的键也应该是 Farmer。

    -----------FETCH DATA (SELECT)-------------------------- 
    
    const users = await Farmer.findAll({
        include: [
          {
            association: Farmer.userProfile,
            include: [Login],
          },
        ],
      });
    
    Output:
    [
        {
            "id": 1,
            "supplierCode": "SUP0001",
            "createdAt": "2021-11-17T07:39:13.000Z",
            "updatedAt": "2021-11-17T07:39:13.000Z",
            "userProfileId": 1,
            "userProfile": {
                "id": 1,
                "firstName": "ssss",
                "middleName": "ssss",
                "lastName": "ssss",
                "address": "ssss",
                "createdAt": "2021-11-17T07:39:13.000Z",
                "updatedAt": "2021-11-17T07:39:13.000Z",
                "loginId": 1,
                "login": {
                    "id": 1,
                    "name": "ssss",
                    "email": "ssss",
                    "password": "ssss",
                    "role": "ssss",
                    "lastLogin": null,
                    "avatar": "ssss",
                    "status": "ssss",
                    "createdAt": "2021-11-17T07:39:13.000Z",
                    "updatedAt": "2021-11-17T07:39:13.000Z"
                }
            }
        }
    ] 
    

    假设您要添加另一个用户角色/类型(我们已经有 Farmer)。然后你可以制作coordinater.js(例如用户角色/类型)并定义属性

    在上面的 index.js 中你可以添加这个关系

    //协调者关系

    db.coordinator.userProfile = db.coordinator.belongsTo(db.userProfile, {
      onDelete: "CASCADE",
      onUpdate: "CASCADE",
    }); 
    

    现在您可以使用不同的用户角色/类型注册用户: Coordinator.create.... 给出正确的关联

    【讨论】:

      猜你喜欢
      • 2019-01-20
      • 2020-12-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-18
      • 1970-01-01
      • 2020-01-17
      • 1970-01-01
      相关资源
      最近更新 更多