Подтвердить что ты не робот

Простой пример отношения "многие ко многим" с использованием Sequelize

Я пытаюсь создать простой пример отношения "многие ко многим" между таблицами, используя Sequelize. Однако это кажется более сложным, чем я ожидал.

Это код, который у меня есть в настоящее время (файл ./db.js экспортирует экземпляр соединения Sequelize).

const Sequelize = require("sequelize");
const sequelize = require("./db");

var Mentee = sequelize.define('mentee', {
    id: {
        type: Sequelize.INTEGER,
        primaryKey: true,
        autoIncrement: true
    },
    name: {
        type: Sequelize.STRING
    }
});

var Question = sequelize.define('question', {
    id: {
        type: Sequelize.INTEGER,
        primaryKey: true,
        autoIncrement: true
    },
    text: {
        type: Sequelize.STRING
    }
});

var MenteeQuestion = sequelize.define('menteequestion', {
//    answer: {
//        type: Sequelize.STRING
//    }
});

// A mentee can answer several questions
Mentee.belongsToMany(Question, { as: "Questions", through: MenteeQuestion });

// And a question can be answered by several mentees
Question.belongsToMany(Mentee, { as: "Mentees", through: MenteeQuestion });

let currentQuestion = null;
Promise.all([
    Mentee.sync({ force: true })
  , Question.sync({ force: true })
  , MenteeQuestion.sync({ force: true })
]).then(() => {
    return Mentee.destroy({where: {}})
}).then(() => {
    return Question.destroy({ where: {} })
}).then(() => {
    return Question.create({
        text: "What is 42?"
    });
}).then(question => {
    currentQuestion = question;
    return Mentee.create({
        name: "Johnny"
    })
}).then(mentee => {
    console.log("Adding question");
    return mentee.addQuestion(currentQuestion);
}).then(() => {
    return MenteeQuestion.findAll({
        where: {}
      , include: [Mentee]
    })
}).then(menteeQuestions => {
    return MenteeQuestion.findAll({
        where: {
            menteeId: 1
        }
      , include: [Mentee]
    })
}).then(menteeQuestion => {
    console.log(menteeQuestion.toJSON());
}).catch(e => {
    console.error(e);
});

При выполнении этого я получаю:

Невозможно добавить ограничение внешнего ключа

Я думаю, что это из-за типа id, однако я понятия не имею, почему он появляется и как мы можем его исправить.

Еще одна ошибка, которая появилась, когда предыдущая из них не появится:

Выполнение (по умолчанию): INSERT INTO menteequestions (menteeId, questionId, createdAt, updatedAt) VALUES (2,1, '2017-03-17 06:18:01', ' 2017-03-17 06:18:01 ');

Ошибка: попечитель не связан с менталитетом!

И еще одна ошибка, которую я получаю - я думаю, что из-за force:true в sync -is:

ТАБЛИЦА DROP IF EXISTS mentees;

ER_ROW_IS_REFERENCED: невозможно удалить или обновить родительскую строку: ограничение внешнего ключа завершено

Как их решить?

Опять же, мне нужен только минимальный пример операции "много ко многим" (в этом случае просто вставлять и читать), но это, кажется, не в моих силах. С этим боролся два дня.

4b9b3361

Ответ 1

Миграция

Я предлагаю вам использовать sequelize migrations вместо sync() для каждой модели. Существует модуль - sequelize.cli, который позволяет легко управлять миграциями и семенами. Это каким-то образом заставляет структуру проекта создавать файл инициализации index.js внутри /models каталога проекта. Он считает, что все ваши определения моделей будут в этом каталоге. Этот script выполняет итерацию по всем файлам модели (каждое определение модели находится в отдельном файле, например mentee.js, question.js), и выполняет sequelize.import(), чтобы присвоить эти модели экземпляру sequelize - это позволяет вам получить к ним доступ позже через sequelize[modelName] например sequelize.question.

Примечание. при создании файлов миграции помните о полях временных меток - createdAt, updatedAt и, в конечном итоге, deletedAt.

Синхронизировать

Лично я использую sync() только при выполнении тестов - это может быть показано в три этапа

  • выполнить sequelize.sync({ force: true }), чтобы синхронизировать все модели
  • запустите некоторую базу данных seeds (также можно выполнить через sequelize-cli),
  • выполнить тесты.

Это очень удобно, потому что вы можете очистить базу данных перед запуском тестов и, чтобы отличить разработку от тестов, тесты могут использовать разные базы данных, например. project_test, так что база данных разработки остается неизменной.

Много-ко-многим

Теперь перейдем к вашей проблеме - отношение m: n между двумя моделями. Прежде всего, из-за того, что вы выполняете Promise.all(), sync может работать в другом порядке, чем вы добавляете в него функции. Чтобы избежать этой ситуации, я предлагаю вам использовать функцию mapSeries обещания Bluebird, которую Sequelize использует и предоставляет под sequelize.Promise (это также является причиной вашей последней ошибки об удалении родительской строки - вы пытаетесь удалить mentees, на который ссылается menteequestion).

sequelize.Promise.mapSeries([
    Mentee.sync({ force: true })
  , Question.sync({ force: true })
  , MenteeQuestion.sync({ force: true })
], (model) => { return model.destroy({ where: {} }); }).then(() => {

});

Первый параметр mapSeries - это массив из promises, но второй - это функция, которая запускается с результатом каждого ранее определенного обещания. Из-за того, что Model.sync() приводит к самой Модели, мы можем выполнить model.destroy() на каждой итерации.

После этого вы можете вставить некоторые данные в базу данных через create(), как в примере. Теперь время, чтобы исправить ошибку: mentee не связано с menteequestion! ошибка. Это происходит потому, что вы связали Mentee с Question, но между menteequestion и Mentee (или Question) нет связи. Чтобы исправить это, после belongsToMany вы можете добавить

MenteeQuestion.belongsTo(Mentee, { foreignKey: 'menteeId' });
MenteeQuestion.belongsTo(Question, { foreignKey: 'questionId' });

Теперь вы можете добавить include: [Mentee, Question] при запросе menteequestion. При выполнении toJSON() вы также запускаете еще одну ошибку, потому что вы выполняете findAll, который возвращает массив экземпляров. Вы можете сделать forEach()

menteeQuestions.forEach(menteeQuestion => {
    console.log(menteeQuestion.toJSON());
});