[英]How to use 3 foreign keys in many-to-many join table in Sequelize
我要這個:
我可以在幾分鍾內使用 SQL 手動完成此操作,這是一個非常常見的場景,所以我不得不認為 Sequelize 中有一種方法。
用戶可以在許多組織中扮演許多角色。 我可能是 Acme 的管理員,但只是 Microsoft 的用戶。 看起來像這樣的數據:
用戶數據:
組織數據:
角色:
當然,我可以把它們放在一起:
select
u.username,
r.name,
o.name
from
"user" u
inner join
user_role_organization uro on u.id = uro.user_id
inner join
organization o on uro.organization_id = o.id
inner join
role r on uro.role_id = r.id
我工作的真實世界 model 看起來像這樣:
const orgModel = {
id: {
type: DataTypes.UUID,
primaryKey: true,
allowNull: false
},
name: {
type: DataTypes.STRING(100),
allowNull: false
}
};
const roleModel = {
id: {
type: DataTypes.UUID,
primaryKey: true,
allowNull: false
},
name: {
type: DataTypes.STRING(100),
allowNull: false
}
};
const userModel = {
id: {
type: DataTypes.UUID,
primaryKey: true,
allowNull: false
},
username: {
type: DataTypes.STRING(100),
allowNull: false
}
};
const organizationUserToRoleModel = {
id : {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true
},
organization_id: {
type: DataTypes.UUID,
allowNull: false
},
role_id: {
type: DataTypes.UUID,
allowNull: false
},
user_id: {
type: DataTypes.UUID,
allowNull: false
}
};
...以及它們各自的關系
auth_user.belongsToMany(auth_organization, { as: "AuthOrganizations", through: organization_to_user_to_role, foreignKey: "user_id" });
auth_organization.belongsToMany(auth_user, { as: "AuthUsers", through: organization_to_user_to_role, foreignKey: "organization_id" });
auth_organization.belongsToMany(role, { as: "Roles", through: organization_to_user_to_role, foreignKey: "organization_id" });
role.belongsToMany(auth_organization, { as: "RoleOrganizations", through: organization_to_user_to_role, foreignKey: "role_id" });
auth_user.belongsToMany(role, { as: "OrganizationUserRoles", through: organization_to_user_to_role, foreignKey: "user_id" });
role.belongsToMany(auth_user, { as: "OrganizationRoleUsers", through: organization_to_user_to_role, foreignKey: "role_id" });
我最終得到了一些看起來正確的東西:
但是,在播種類似數據時出現以下錯誤:
ValidationErrorItem {
message: 'organization_id must be unique',
type: 'unique violation',
path: 'organization_id',
value: '385e2860-094d-11ed-a072-25e64f3c77e7',
origin: 'DB',
instance: null,
validatorKey: 'not_unique',
validatorName: null,
validatorArgs: []
}
毫無意義,除了“id”之外的任何東西都需要在該表中是唯一的,不是嗎? 我想這是因為它是外鍵而強制唯一性? 我使用這樣填充的值來實現這一點:
let acmeOrg = await auth_organization.findOne({ where: { name: "ACME Corp." } });
let fakeOrg = await auth_organization.findOne({ where: { name: "Fake, Inc." } });
let user1 = await auth_user.findOne({ where: { username: "user1" } });
let user2 = await auth_user.findOne({ where: { username: "user2" } });
let ownerRole = await role.findOne({ where: { name: "Owner" } });
let adminRole = await role.findOne({ where: { name: "Admin" } });
let userRole = await role.findOne({ where: { name: "User" } });
await user1.addAuthOrganizations(acmeOrg,
{
through: {
role_id: ownerRole.id
}
});
await user2.addAuthOrganizations(acmeOrg,
{
through: {
role_id: adminRole.id
}
});
await user1.addAuthOrganizations(fakeOrg,
{
through: {
role_id: userRole.id
}
});
我擁有比 Sequelize 更多的歷史 w/ 關系數據。 我還為連接表嘗試了這個 model,它創建了一個更奇怪的 model,它在 user_id 和 organization_id 字段上強制使用復合主鍵,即使我設置了 primaryKey: false。
編輯 1 :
我懷疑這完全取決於我如何為 model 構建 FK,只是來自之前的 Sequelize 冒險。 我只是嘗試將 unique 設置為 false 並像這樣設置 FKs - 它現在抱怨“user_id”必須是唯一的,即使那不是真的,至少根據我的意圖。
let organizationUserToRoleModel = {
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true
},
organization_id: {
type: DataTypes.UUID,
allowNull: false,
unique: false
},
role_id: {
type: DataTypes.UUID,
allowNull: false,
unique: false
},
user_id: {
type: DataTypes.UUID,
allowNull: false,
unique: false
}
};
auth_user.belongsToMany(auth_organization, { as: "AuthUserOrganizations", through: organization_to_user_to_role, foreignKey: "user_id" });
auth_organization.belongsToMany(auth_user, { as: "OrganizationAuthUsers", through: organization_to_user_to_role, foreignKey: "organization_id" });
auth_organization.belongsToMany(role, { as: "AuthOrganizationRoles", through: organization_to_user_to_role, foreignKey: "organization_id" });
role.belongsToMany(auth_organization, { as: "RoleAuthOrganizations", through: organization_to_user_to_role, foreignKey: "role_id" });
編輯 2:
找到了原因,無論我對 model 做什么。外鍵都添加了唯一約束:這是連接表的最新 model:
let organizationUserToRoleModel = {
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true
},
organization_id: {
type: DataTypes.UUID,
allowNull: true,
constraints: false,
unique: false
},
role_id: {
type: DataTypes.UUID,
allowNull: true,
constraints: false,
unique: false
},
user_id: {
type: DataTypes.UUID,
allowNull: true,
constraints: false,
unique: false
}
};
但是,當我檢查結果時它們仍然被創建:
ALTER TABLE auth.organization_to_user_to_role ADD CONSTRAINT organization_to_user_to_role_organization_id_role_id_key UNIQUE (organization_id, role_id)
ALTER TABLE auth.organization_to_user_to_role ADD CONSTRAINT organization_to_user_to_role_user_id_key UNIQUE (user_id)
如果我手動刪除它們,我可以播種預期的數據並查詢它 w/oa 問題,如下所示:
select
u.username
from
auth_user u
inner join
organization_to_user_to_role our
on u.id = our.user_id
inner join
auth_organization ao
on ao.id = our.organization_id
inner join
"role" r
on r.id = our.role_id
我覺得我非常接近,但不確定如何防止創建 FK 約束。 將約束設置為 false 在這里似乎沒有任何作用。 我想我可以在事后對它們的刪除進行編碼,但這似乎很老套而且不正確。
編輯 3:
我已經在 model 本身上嘗試了一些不同的東西,以及鍵之間的關系,但是我得到了完全相同的結果和完全相同的唯一約束。 如果我什至可以讓它在所有 3 個鍵上設置一個唯一約束(現在它們都是復合鍵的一部分),那就足夠了。
當前 model,我更喜歡:
let organizationUserToRoleModel = {
organization_id: {
type: DataTypes.UUID,
primaryKey: true,
constraints: false,
unique: false
},
role_id: {
type: DataTypes.UUID,
primaryKey: true,
constraints: false,
unique: false
},
user_id: {
type: DataTypes.UUID,
primaryKey: true,
constraints: false,
unique: false
}
};
似乎“約束”和“獨特”的效果為零。 與我之前的嘗試相比,這唯一的區別是復合鍵比無用的自動增量 PK 更有意義。
讓我看看我是否可以簡化問題……您有三個 Postgres 表:組織、角色和用戶。 每個都有唯一的標識符。
一個用戶在給定組織中只允許一個角色,但可以與多個組織相關聯,每個組織具有不同的角色(您的示例是 Acme 的管理員,但 Microsoft 的用戶)。
Sequelize 中的模型需要是什么樣子才能實現這一點?
這對嗎?
看起來你到了那里。
通常,在 model 中提供一組 FK 值的任何表都要求將值列本身聲明為唯一(至少)。 在某些模型中,提供鍵值的表將主鍵設置為實際數據值,而不是 ID 列。
您的設計有問題,請參閱此處的 James Barton 回答。 我建議您使用以下表格而不是 User_Role_Organization:
角色_用戶
組織_角色
如果一個角色屬於多個組織,那么所有角色用戶也屬於那些可能不是預期的組織。
答案是強迫桌子按我想要的方式工作。 它在 Sequelize 中運行良好。
遷移使 Sequelize 中的更改和讀/寫工作完美無缺。 所以,我想只是一個錯誤。 大多數 ORM 似乎都有這樣有趣的邊緣情況。
await queryInterface.createTable("organization_to_user_to_role", {
organization_id: {
type: Sequelize.DataTypes.UUID,
primaryKey: true,
constraints: false
},
role_id: {
type: Sequelize.DataTypes.UUID,
primaryKey: true,
constraints: false
},
user_id: {
type: Sequelize.DataTypes.UUID,
primaryKey: true,
constraints: false
}
});
queryInterface.addConstraint("organization_to_user_to_role", {
type: "FOREIGN KEY",
name: "organization_to_user_to_role_organization_id_fkey",
fields: ["organization_id"],
references: {
table: "auth_organization",
field: "id"
}
});
queryInterface.addConstraint("organization_to_user_to_role", {
type: "FOREIGN KEY",
name: "organization_to_user_to_role_user_id_fkey",
fields: ["user_id"],
references: {
table: "auth_user",
field: "id"
}
});
queryInterface.addConstraint("organization_to_user_to_role", {
type: "FOREIGN KEY",
name: "organization_to_user_to_role_role_id_fkey",
fields: ["role_id"],
references: {
table: "role",
field: "id"
}
});
如果您生成一個新數據庫,則必須刪除不需要的約束,如果它們存在,我將它們放入遷移中:
queryInterface.removeConstraint(
"organization_to_user_to_role"
"organization_to_user_to_role_organization_id_key"
);
queryInterface.removeConstraint(
"organization_to_user_to_role",
"organization_to_user_to_role_role_id_user_id_key"
);
好的,我明白這個問題。 我將給出相同的示例:在 test.js 中
const module = require('./module')(sequelize, DataTypes)
const permission = require('./permission')(sequelize, DataTypes)
const role = require('./role')(sequelize, DataTypes)
const test = sequelize.define('test', {
moduleId: {
type: DataTypes.INTEGER(4),
ref: {
model: module,
key: 'id'
}
},
roleId: {
type: DataTypes.INTEGER(4),
ref: {
model: role,
key: 'id'
}
},
permissionId: {
type: DataTypes.INTEGER(4),
ref: {
model: permission,
key: 'id'
}
},
createdDate: {
type: DataTypes.DATE,
defaultValue: DataTypes.NOW
}
}, {
timestamps: false
});
test.belongsTo(module, { foreignKey: 'moduleId' });
test.belongsTo(permission, { foreignKey: 'permissionId' });
test.belongsTo(role, { foreignKey: 'roleId' });
module.hasMany(test, { foreignKey: 'moduleId' });
permission.hasMany(test, { foreignKey: 'permissionId' });
role.hasMany(test, { foreignKey: 'roleId' });
return test;
現在,我們只能添加一個唯一約束
const queryInterface = sequelize.getQueryInterface();
sequelize.sync({force: true}).then(()=>{
return queryInterface.addConstraint('tests', {
fields:['moduleId', 'permissionId', 'roleId'],
type: 'unique',
name: 'custom_unique_constraint'
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.