Я пытаюсь создать ассоциацию «многие ко многим» в Sequelize (и Postgres
), используя подход belongsToMany
, но у меня возникли некоторые проблемы. Сценарий действительно распространен и включает три таблицы: t_users
(название модели User
), t_roles
(название модели Role
и их таблицу ссылок t_user_role
(название модели UserRole
).
Стоит отметить, что все таблицы, включая таблицу ссылок, были ранее созданы с использованием node-db-migrate (при необходимости я могу предоставить код миграции).
Перед объяснением проблемы , задействованные файлы:
Модель пользователя
class User extends Sequelize.Model {
constructor(props) {
super(props);
}
}
const dbUser = {
a_id: {
primaryKey: true,
autoIncrement: true,
type: Sequelize.BIGINT,
},
a_date_created: {
type: Sequelize.DATE,
allowNull: false,
},
a_first_name: {
type: Sequelize.STRING,
allowNull: false,
},
a_last_name: {
type: Sequelize.STRING,
allowNull: false,
},
a_email: {
type: Sequelize.TEXT,
unique: true,
allowNull: false,
},
a_password: {
type: Sequelize.STRING,
allowNull: false,
},
a_birthday: {
type: Sequelize.DATE,
allowNull: false,
},
a_is_active: {
type: Sequelize.BOOLEAN,
allowNull: false,
},
};
User.init(dbUser, {
sequelize: db, // db holds the sequelize connection instance
modelName: 't_user',
timestamps: false,
tableName: 't_users',
});
User.associate = (models) => {
User.belongsToMany(models.Role, {
through: { model: UserRole, unique: false },
as: 'roles',
foreignKey: 'a_user',
otherKey: 'a_role',
});
};
Ролевая модель
class Role extends Sequelize.Model {
constructor(props) {
super(props);
}
}
const dbRole = {
a_id: {
primaryKey: true,
autoIncrement: true,
type: Sequelize.BIGINT,
},
a_role: {
type: Sequelize.STRING,
allowNull: false,
},
};
Role.init(dbRole, {
sequelize: db,
modelName: 't_role',
timestamps: false,
tableName: 't_roles',
});
Role.associate = (models) => {
Role.belongsToMany(models.User, {
through: { model: UserRole, unique: false },
as: 'UserOfRoles',
foreignKey: 'a_role',
otherKey: 'a_user',
});
};
UserRole
class UserRole extends Sequelize.Model {
constructor(props) {
super(props);
}
}
const dbUserRole = {
a_id: {
primaryKey: true,
autoIncrement: true,
type: Sequelize.BIGINT,
},
a_role: {
type: Sequelize.BIGINT,
allowNull: false,
primaryKey: false,
references: {
model: Role,
key: 'a_id',
},
onDelete: 'CASCADE',
},
a_user: {
type: Sequelize.BIGINT,
allowNull: false,
references: {
model: User,
key: 'a_id',
},
onDelete: 'CASCADE',
},
};
UserRole.init(dbUserRole, {
sequelize: db,
modelName: 't_user_role',
timestamps: false,
tableName: 't_user_role',
});
Все ассоциации вызываются в моих моделях index.js
Модели
import User from './User';
import UserRole from './UserRole';
import Role from './Role';
const models = {
User,
Role,
UserRole,
};
Object.keys(models).forEach((modelName) => {
if (models[modelName].associate) {
models[modelName].associate(models);
}
});
export default models;
С учетом сказанного , теперь проблема.
Я пытаюсь запросить таблицу User
и получить пользователя и, используя include
, также roles
, связанный с пользователем. Но по какой-то причине все результаты, которые я получаю, не зависят от роли пользователя.
Используемый мной запрос
const result = await User.findAll({
where: {
a_id: 1, // For simplicity
},
include: [
{
model: models.Role, // The Role model class
as: 'roles',
attributes: ['a_role'],
through: {
attributes: [],
},
},
],
});
Этот запрос возвращает мне следующий объект par sed как JSON (даже если я не разбираю его, ключ roles
отсутствует в простом объекте ответа sequelize):
[
{
"a_id": 1,
"a_date_created": "2020-05-24T00:00:00.000Z",
"a_first_name": "Thiago",
"a_last_name": "moreira",
"a_email": "thiago@mail.com",
"a_password": "encrypted stuff",
"a_birthday": "1991-09-04T00:00:00.000Z",
"a_is_active": true
}
]
Запрос, который Sequelize выводит
SELECT "t_user"."a_id",
"t_user"."a_date_created",
"t_user"."a_first_name",
"t_user"."a_last_name",
"t_user"."a_email",
"t_user"."a_password",
"t_user"."a_birthday",
"t_user"."a_is_active",
"roles"."a_id" AS "roles.a_id",
"roles"."a_role" AS "roles.a_role",
"roles->t_user_role"."a_id" AS "roles.t_user_role.a_id",
"roles->t_user_role"."a_role" AS "roles.t_user_role.a_role",
"roles->t_user_role"."a_user" AS "roles.t_user_role.a_user"
FROM "t_users" AS "t_user"
LEFT OUTER JOIN ( "t_user_role" AS "roles->t_user_role"
INNER JOIN "t_roles" AS "roles" ON "roles"."a_id" = "roles->t_user_role"."a_role"
)
ON "t_user"."a_id" = "roles->t_user_role"."a_user";
(Это raw query
и отлично работает, когда я использую его в Postgres
)
Согласно моим показаниям около inte rnet, findAll
метод, если он предоставлен с include
, должен также иметь возможность возвращать данные ассоциации. Чтобы сделать все немного более странным, есть два сценария ios, в которых я могу получить роль пользователя (но если я правильно понял документацию, эти «обходные пути» не понадобятся).
Первый сценарий
const result = await User.findAll({
where: {
a_id: 1,
},
raw: true, // here is the difference from the previous approach
nest: true, // here is the difference from the previous approach
include: [
{
model: models.Role,
as: 'roles',
attributes: ['a_role'],
through: {
attributes: [],
},
},
],
});
Возвращает
[
{
"a_id": 1,
"a_date_created": "2020-05-24",
"a_first_name": "Thiago",
"a_last_name": "moreira",
"a_email": "thiago@mail.com",
"a_password": "encrypted stuff",
"a_birthday": "1991-09-04",
"a_is_active": true,
"roles": {
"a_role": "admin",
"t_user_role": {
"a_id": 1,
"a_role": "1",
"a_user": "1"
}
}
}
]
Второй сценарий
const result = await User.findAll({
where: {
a_id: 1,
},
});
// Here I get the first instance of the findAll return and
// use the getRoles function generated automatically by the
// Sequelize association.
JSON.stringify(await result[0].getRoles(), null, 2);
Он возвращает
[
{
"a_id": 1,
"a_role": "admin"
}
]
(при таком подходе мне нужно было бы объединить этот результат с экземпляром User
).
Что мне до сих пор не удается понять, так это то, что из-за в этих двух сценариях ios связанные данные, кажется, присутствуют, но по какой-то причине они не возвращаются с использованием «общего» подхода (который, кажется, используется почти в каждом учебнике по Sequelize).
Мне очень жаль ОГРОМНЫЙ вопрос, но, поскольку этот топи c сводит меня с ума, я хотел предоставить весь необходимый контекст.