Объединение в Sequelize

#javascript #node.js #sequelize.js

#javascript #node.js #sequelize.js

Вопрос:

Я пытаюсь получить данные, как показано ниже:

 "data": {
    "religions": {
        "Major1Name": {
            "AttendanceYear1Name": {
                "student": [ {...}, {...}, {...} ]
            },
            "AttendanceYear2Name": {
                "student": [ {...}, {...}, {...} ]
            }
        },
        "Major2Name": {
            "AttendanceYear1Name": {
                "student": [ {...}, {...}, {...} ]
            },
            "AttendanceYear2Name": {
                "student": [ {...}, {...}, {...} ]
            }
        }
    }
}
 

Я знаю, как настроить базовый уровень ассоциаций, например. студент и майор. Но, насколько я знаю базу данных, я понятия не имею, как связать с religions and majors , а также in Sequelize . Пожалуйста, помогите.

У меня есть следующие таблицы:

  1. основные
  2. посещаемость лет
  3. религии
  4. Для студентов
  5. зачисления

Ниже приведены мои модели.

основные

 'use strict';

const { Model } = require('sequelize');

module.exports = (sequelize, DataTypes) => {
  class Major extends Model {
    static associate(models) {
      Major.hasMany(models.Enrollment, {
        foreignKey: 'majorId',
        as: 'major',
      });
    }
  }

  Major.init(
    {
      majorId: {
        allowNull: false,
        autoIncrement: true,
        field: 'major_id',
        primaryKey: true,
        type: DataTypes.INTEGER,
      },
    { ... }
    },
    {
      sequelize,
      modelName: 'Major',
      tableName: 'majors',
    }
  );

  return Major;
};

 

посещаемость лет

 "use strict";

const { Model } = require("sequelize");

module.exports = (sequelize, DataTypes) => {
  class AttendanceYear extends Model {
    static associate(models) {
      AttendanceYear.hasMany(models.Enrollment, {
        as: "enrollments",
        foreignKey: "attendance_year_id",
      });
    }
  }

  AttendanceYear.init(
    {
      attendanceYearId: {
        allowNull: false,
        autoIncrement: true,
        field: "attendance_year_id",
        primaryKey: true,
        type: DataTypes.INTEGER,
      },
    { ... }
    },
    {
      sequelize,
      modelName: "AttendanceYear",
      tableName: "attendance_years",
    }
  );

  return AttendanceYear;
};

 

religions

 "use strict";

const { Model } = require("sequelize");

module.exports = (sequelize, DataTypes) => {
  class Religion extends Model {
    static associate(models) {
      Religion.hasMany(models.Student, {
        foreignKey: "religionId",
        as: "student",
      });
    }
  }

  Religion.init(
    {
      religionId: {
        allowNull: false,
        autoIncrement: true,
        field: "religion_id",
        primaryKey: true,
        type: DataTypes.INTEGER,
      },
      { ... }
    },
    {
      sequelize,
      modelName: "Religion",
      tableName: "religions",
    }
  );

  return Religion;
};

 

students

 'use strict';

const { Model } = require('sequelize');

module.exports = (sequelize, DataTypes) => {
  class Student extends Model {
    static associate(models) {

      Student.belongsTo(models.Religion, {
        foreignKey: 'religionId',
        as: 'religion',
        targetKey: 'religionId',
      });

      Student.belongsTo(models.Enrollment, {
        foreignKey: 'studentId',
        as: 'enrollment',
      });
    }
  }

  Student.init(
    {
      studentId: {
        allowNull: false,
        autoIncrement: true,
        field: 'student_id',
        primaryKey: true,
        type: DataTypes.INTEGER,
      },
      name: {
        allowNull: false,
        field: 'name_en',
        type: DataTypes.STRING(50),
      },
      religionId: {
        allowNull: false,
        field: 'religion_id',
        references: {
          model: 'religons',
          key: 'religion_id',
        },
        type: DataTypes.INTEGER,
      },
    },
    {
      sequelize,
      modelName: 'Student',
      tableName: 'students',
    }
  );
  return Student;
};
 

and enrollments

 'use strict';

const { Model } = require('sequelize');

module.exports = (sequelize, DataTypes) => {
  class Enrollment extends Model {
    static associate(models) {

      Enrollment.belongsTo(models.Major, {
        foreignKey: 'majorId',
        as: 'major',
      });

      Enrollment.belongsTo(models.Student, {
        foreignKey: 'studentId',
        as: 'student',
      });

      Enrollment.belongsTo(models.AttendanceYear, {
        foreignKey: 'attendanceYearId',
        as: 'attendanceYear',
      });
    }
  }

  Enrollment.init(
    {
      enrollmentId: {
        allowNull: false,
        autoIncrement: true,
        field: 'enrollment_id',
        primaryKey: true,
        type: DataTypes.INTEGER,
      },
      majorId: {
        allowNull: false,
        field: 'major_id',
        onDelete: 'NO ACTION',
        onUpdate: 'CASCADE',
        references: {
          model: 'majors',
          key: 'major_id',
        },
        type: DataTypes.INTEGER,
      },
      studentId: {
        allowNull: false,
        field: 'student_id',
        onDelete: 'CASCADE',
        onUpdate: 'CASCADE',
        references: {
          model: 'students',
          key: 'student_id',
        },
        type: DataTypes.INTEGER,
      },
      attendanceYearId: {
        allowNull: false,
        field: 'attendance_year_id',
        onDelete: 'NO ACTION',
        onUpdate: 'CASCADE',
        references: {
          model: 'attendance_years',
          key: 'attendance_year_id',
        },
        type: DataTypes.INTEGER,
      },
    },
    {
      sequelize,
      modelName: 'Enrollment',
      tableName: 'enrollments',
    }
  );

  return Enrollment;
};

 

What I’ve done and doesn’t work

 const religions = await models.Religion.findAll({
    where: { religionId: req.params.religionId },
    include: [
      {
        model: models.Major,
        as: 'major',
        include: [
          {
            model: models.AttendanceYear,
            as: 'attendanceYear',
            include: [
              {
                model: models.Student,
                as: 'student',
                attributes: ['studentId', 'nameMm', 'nameEn', 'nrc'],
                include: [
                  {
                    model: models.Parent,
                    as: 'parent',
                    attributes: ['fatherNameMm', 'fatherNameEn', 'fatherNrc'],
                  },
                  {
                    model: models.Enrollment,
                    as: 'enrollment',
                    attributes: ['rollNo'],
                    where: {
                      academicYearId: req.params.academicYearId,
                    },
                  },
                ],
              },
            ],
          },
        ],
      },
    ],
  });
 

Error

 SequelizeEagerLoadingError: Major is not associated to Religion!
 

Updated

I have the followings models (which will be the tables in the database) files in this structure src/database/models/ :

  1. majors
  2. attendance_years
  3. religions
  4. students
  5. enrollments

The whold structure is:

 database/migrations/....js
database/models/....js
database/seeders/....js
 

У меня есть index.js файл внутри этого models/ каталога, и он выглядит так, как показано ниже:

 'use strict';

const config = require('../../config/config');
const fs = require('fs');
const path = require('path');
const { Sequelize, DataTypes } = require('sequelize');
const basename = path.basename(__filename);
const db = {};

const logger = require('../../startup/logger')();

const ENV = config[process.env.NODE_ENV];

let sequelize;
sequelize = new Sequelize(ENV.database, ENV.username, ENV.password, {
  dialect: 'mysql',
  host: ENV.host,
  define: {
    charset: 'utf8',
    collate: 'utf8_general_ci',
    timestamps: false, // omit createdAt and updatedAt
  },
});

sequelize
  .authenticate()
  .then(() => {
    // logger.info('Connected to the database.');
    console.log('Connected to the database.');
  })
  .catch((error) => {
    logger.error('Unable to connect to the database.', error);
    console.log(`Unable to connect to the database.`, error);
    process.exit(1);
  });

fs.readdirSync(__dirname)
  .filter((file) => {
    return (
      file.indexOf('.') !== 0 amp;amp; file !== basename amp;amp; file.slice(-3) === '.js'
    );
  })
  .forEach((file) => {
    const model = require(path.join(__dirname, file))(sequelize, DataTypes);
    db[model.name] = model;
  });

db.sequelize = sequelize;
db.Sequelize = Sequelize;

Object.keys(db).forEach((modelName) => {
  if (db[modelName].associate) {
    db[modelName].associate(db);
  }
});

module.exports = db;
 

С этой реализацией мне не нужно импортировать требуемые модели внутри файлов моделей, а также обработчики маршрутов, и мне просто нужна следующая строка.

 const models = require('../database/models');
/** I can easily get model instance by calling models.Student to get Student model. **/
 

И причина, по которой я не использую этот sync подход, заключается в том, что я боюсь случайно потерять свои данные в процессе производства, если я обновлю модели или добавлю новую. Поэтому я использовал sequelize-cli . С его помощью я могу превратить свои модели в таблицы, запустив sequelize db:migrate .

Причина, по которой я явно определил атрибут и имя таблицы, заключается в том, что я хочу, чтобы они следовали соглашениям об именовании MySQL: attendance_years и attendance_year_id , например. Но когда я запускаю вызовы к базе данных, я вижу множество псевдонимов именования в терминале: attendance_year_id как attendanceYearId и т.д. Я думаю, что это может повлиять на производительность запросов, и поэтому я подумаю о том, чтобы позволить sequelize полностью управлять соглашениями об именах.

Комментарии:

1. Как бы вы их связали? 1 к 1 ? от n до n ? от 1 до n ?

2. @AmadouBeye Имеет ли смысл делать это для достижения примерной структуры, которую я указал в верхней части сообщения? РЕЛИГИИ имеют много основных направлений. Я думаю, что это связано проволокой.

Ответ №1:

Спасибо, что обратились ко мне в Twitter. Я действительно ценю это. При этом давайте посмотрим, сможем ли мы ответить на ваш вопрос. Пожалуйста, позвольте мне немного разобраться, прежде чем перейти к решению, которое я хочу предоставить.

Во-первых, несколько советов из моего личного опыта.

  1. Sequelize очень мощный. И под капотом это решает для вас множество проблем, что означает, что вам не нужно беспокоиться о многих вещах, таких как атрибуты первичного ключа, имена столбцов внешнего ключа, имена таблиц и т. Д. За исключением некоторых действительно сложных ассоциаций или в некоторых крайних случаях (например, если вы работаете с устаревшей базой данных, например), вам действительно не нужно явно указывать, что это такое, а скорее позволить sequelize сделать тяжелую работу за вас. Я упоминаю об этом, потому что я заметил, что вы пытались primaryKey указать атрибуты и включили tableName атрибут в объект options ваших определений моделей, что нормально, но действительно не нужно, и на самом деле может фактически мешать запросам движков sequelize, и в этом случае вам, возможно, придется определять эти атрибуты везде, и это просто беспорядок. Sequelize генерирует primaryKey атрибуты и tableName s по умолчанию — поэтому, если вы можете, пожалуйста, максимально сведите к минимуму ненужные определения — смотрите, почему из документации по выводу имен таблиц здесь. Если вы чувствуете необходимость иметь свой собственный пользовательский ключ для моделей, рассмотрите возможность использования атрибута UUID, например.
 // Custom UUID attribute seperate from the model id but also generated by sequelize.
studentUUID: {
  type: DataTypes.UUID,
  defaultValue: Sequelize.UUIDV4 // will generate a UUID for every created instance
}
 

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

  1. Я также заметил, что вы пытались определить ассоциации моделей в статических методах в определениях моделей. Я не уверен, почему вы это сделали, но я не думаю, что именно так определяются ассоциации, как из моего опыта, так и из официальных документов sequelize (версия 6 — как показано здесь). Скорее, это делается так, что ассоциации определяются в файле модели после инициализации модели перед экспортом, например
 const { Model, Sequelize, Datatypes } = require('sequelize');
const db = require('../path/to/sequelizeConnectionInstance');

// Let's say we want to associate Religion model to Major model in a 1 - N relationship;
// To do that, we import the Major model 
const Major = require('./Major'); 

class Religion extends Model { /* Yes, it's an empty object and that's okay */ }

Religion.init({
  // Model attributes are defined here
  name: {
    type: DataTypes.STRING,
    allowNull: false
  },
  founder: {
    type: DataTypes.STRING
    // allowNull defaults to true
  }, 
  {...}
}, {
  // Other model options go here, but you rarely need more than the following 2
  sequelize: db, // We need to pass the connection instance
  modelName: 'religion' // I use small letters to avoid ambiguity. you'll see why in a bit
  // No table name attribute is required. the table "religions" is automatically created
});

// The relationship(s) is/are then defined below
Religion.hasMany(Major);
Major.belongsTo(Religion); // The Major model must have a religionId attribute
/* 
*  Sequelize will automagically associate Major to Religion even without the FK being   
*  explicitly described in the association. What sequelize does is find the `modelName id` 
*  attribute in the associated model. i.e. if `Foo.hasMany(Bar)` and `Bar.belongsTo(Foo)`, *  sequelize will look for the `FooId` property in the Bar model, unless you specifiy 
*  otherwise. Also, the convention I use (and what I've found to work) is to import 
*  'child' models to 'parent' model definitions to make 
*  associations.
*/
// THEN we export the model 
modules.export = Religion;

 

Also worth keeping in mind is that, when you associate model entities, sequelize will automatically pluralize the name of the entity in the results, depending on the relationship (i.e. if the parent entity hasMany of the child entity), and returns the results as an array. e.g. if Religion.hasMany(Major) , the result will return religion.majors = [/*an array of majors*/] .

  1. From the above example, you can begin to see some changes that may fix your problem right off the bat. But one last thing I feel to mention before proposing my solution. Again, not unrelated to the previous point, I notice you tried specifying references on some attributes. You don’t need to do this. That’s kind of a NoSQL thing. It’s sufficient enough to define the attribute and it’s type only, and when the association is made, sequelize will figure out the foreign key. You can also specify other details in the association. So for instance; Assuming a 1 — N relationship between Religion and Major models —

in Major.js model file you can specify the religion FK like this

 class Major extends Model {}
Major.init(
  {
     religionId: {
        type: Datatypes.INTEGER, 
        allowNull: true, // set to false if the value is compulsory
        // that's all folks. no other details required.
     }, 
     {/* ...otherAttributes */}
  }, 
  {/* ...options, etc */}
)

module.exports = Major;
 

Then in Religion.js

 const Major = require('./Major');
Religion.init(
  {
// just declare religions OWN attributes as usual  
     {/* ...religionObjectAttributes */}
  }, 
  {/* ...options, etc */}
)
Religion.hasMany(Major, {
  foreignKey: 'religionId',
  onDelete: 'NO ACTION', // what happens when you delete Major ? 
  onUpdate: 'CASCADE',
})

Major.belongsTo(Religion, {
  foreignKey: 'religionId',
})

module.exports = Religion;
 

As a side note, you very often don’t have to include the onDelete and onUpdate attributes in the association, as the defaults are well suited for most use cases. Also worth noting, you can have multiple relationships, in which case you can use aliases. But that doesn’t seem to be necessary or relevant to your question (at least from the onset), but still worth noting and very useful.

That being said. Now to your question: (possible solution 1 — The Easy Way)

The very first thing you need to do is define exactly what the structure of the relationships between the Entities will be like. From the data object, it appears to me to be something like

  • Religion to Major: 1 to N (One religion has Many Majors)
  • Major to AttendanceYear: 1 to N (One Major has Many attendance years)
  • AttendanceYear to Student: 1 to N (One Attendance Year has many students)
    thus, I imagine your desired sequelize response to be something like this:
 religions: [ // array of religions since your'e fetching multiple
  {
     id: 1, // the religion Id
     name: string, // name of religion or whatever
     /*... any other religion attributes */
     majors: [ // array since each religion has multiple majors
        {
           id: 1, // the major Id 
           name: string, // the name of the major or whatever
           /*... any other major attributes */
           attendanceYears: [ // array since each major has multipl
              {
                  id: 1, // the first attendanceYear id
                  name: string, // name of first attendanceYear
                   /*... any other attendanceYear attributes */
                  students: [ // array since ...
                      {
                         id: 1, // student id
                         name: string, // student name
                         /*... any other student attributes */
                      },
                      {
                         id: 2, // id of second student
                         name: string, // 2nd student name
                          /*... any other student attributes */
                      }, 
                      {
                         id: 3, // id of 3rd student
                         name: string, // 3rd student name
                          /*... any other student attributes */
                      }, 
                  ]
              }, 
              {
                  id: 2, // the second attendanceYear id
                  name: string, // name of second attendanceYear
                   /*... other attributes of 2nd attendance year */
                  students: [
                      {
                         id: 4, // student id
                         name: string, // student name
                         /*... any other student attributes */
                      },
                      {
                         id: 5, // id of second student
                         name: string, // 2nd student name
                          /*... any other student attributes */
                      }, 
                      {
                         id: 6, // id of 3rd student
                         name: string, // 3rd student name
                          /*... any other student attributes */
                      }, 
                  ]
              }
           ]
        }, 
        {/*... this is another major instance in majors array */}
     ]
  }, 
  {/*... this is another religion instance in religions array*/}
]
 

Хорошо. Я не уверен, что это то, к чему вы стремитесь, но, исходя из приведенного вами примера, это то, с чем я работаю. Для кода, во-первых, некоторые конфигурации, которые помогут вам в дальнейшем

  1. сохраните экземпляр подключения sequelize db в отдельном файле. Я называю это db.js
 const { Sequelize } = require('sequelize');

module.exports = new Sequelize('dbName', 'dbUsername', 'dbPassword', {
  host: 'localhost',
  dialect: 'mysql', // or whatever dialect you're using
});
 

Я помещаю это здесь сейчас, просто чтобы было ясно, что я имею в виду при использовании db переменной в другом месте. Затем мы создаем модели
Religion.js

 const { Model, Sequelize, DataTypes } = require('sequelize');
const db = require('../path/to/db.js');
// import any models to associate
const Student = require('./Student'); 

class Religion extends Model {}
Religion.init(
   {
     /* religion only attrs, let sequelize generate id*/
   }, 
   {
      sequelize: db, 
      modelName: 'religion'
   }
)
// make association
Religion.hasMany(Student);
Student.belongsTo(Religion);
module.exports = Religion;
 

Major.js

 const { Model, Sequelize, DataTypes } = require('sequelize');
const db = require('../path/to/db.js');
// import any models to associate
const Enrollment = require('./Enrollment');
class Major extends Model {}

Major.init(
   {
      /* major only attrs, let sequelize generate id*/
   }, 
   {
      sequelize: db, 
      modelName: 'major'
   }
)
Major.hasMany(Enrollment)
Enrollment.belongsTo(Major);
module.exports = Major;
 

Student.js

 const { Model, Sequelize, DataTypes } = require('sequelize');
const db = require('../path/to/db.js');
const Enrollment = require('./Enrollment');
class Student extends Model {}

Student.init(
   {
      religionId: {
          type: DataTypes.INTEGER,
      },
      /* other student attrs, let sequelize generate id attr */
   }, 
   {
      sequelize: db, 
      modelName: 'student'
   }
)
Student.hasMany(Enrollment);
Enrollment.belongsTo(Student);
module.exports = Student;
 

Enrollment.js

 const { Model, Sequelize, DataTypes } = require('sequelize');
const db = require('../path/to/db.js');
class Enrollment extends Model {}

Enrollment.init(
   {
      attendanceYearId: {
          type: DataTypes.INTEGER,  // FK for attendanceYear
      }, 
      studentId: {
          type: DataTypes.INTEGER, // FK for student
      }, 
      majorId: {
          type: DataTypes.INTEGER, // FK for major
      },
      /* other 'Major' attrs, let sequelize generate id attr */
   }, 
   {
      sequelize: db, 
      modelName: 'enrollment'
   }
)
module.exports = Enrollment;
 

AttendanceYear.js

 const { Model, Sequelize, DataTypes } = require('sequelize');
const db = require('../path/to/db.js');
const Enrollment = require('./Enrollment');
class AttendanceYear extends Model {}

AttendanceYear.init(
   {
      /* attendanceYear attrs, let sequelize generate id attr */
   }, 
   {
      sequelize: db, 
      modelName: 'attendanceYear'
   }
)

AttendanceYear.hasMany(Enrollment);
Enrollment.belongsTo(AttendanceYear);
module.exports = AttendanceYear;
 

И при этом все ваши объекты настроены на получение данных в той форме, которую вы запросили. например (использование в функции)

someOtherFile.js

 // First import all the models you may wish to use i.e. 
const db = require('../path/to/db.js');
const Religion = require('../path/to/models/Religion');
const Major = require('../path/to/models/Major');
const AttendanceYear = require('../path/to/models/AttendanceYear');
const Student = require('../path/to/models/Student');
const Enrollment = require('../path/to/models/Enrollment');

// Uncomment the line below to update db structure if model changes are made
// db.sync({alter: true}) 

/* query function starts here */
const religions = await Religion.findAndCountAll({
   // If you want all religions then you don't need a where object
   // Also "where: {id: someValue}" should get only 1 result
   include: [{model: Major, include: [{ model: Enrollment, include:[AttendanceYear, Student]}]}]
})
 

Стоит отметить, что если вы собираетесь искать что-то, используя его первичный ключ, то .findByPK(idValueOrVariable) это намного лучше для этого, и вы также можете передать includes и другие параметры и т.д.
При этом, надеюсь, это проливает некоторый свет на то, как работает sequelize и как вы можете подойти к проблеме; Однако у меня такое ощущение, что это не то, к чему вы стремитесь, а если нет, то это, по крайней мере, закладывает основу для 2-го решения, которое я предлагаю.

Возможное решение 2: реструктуризация

Второе решение, на мой взгляд, является тем, которое, по моему мнению, более точно решает проблему. из ваших определений модели (и немного подумав), кажется, что это должно быть —

  • у каждого Major есть много Enrollment s и наоборот, N — N (потому что у студента может быть несколько специальностей в одной и той же регистрации)
  • у каждого AttendanceYear есть много Enrollment s, 1 — N
  • у каждого Religion есть много Student s, 1 — N,
  • у каждого Student может быть много Enrollment s (и, соответственно, Major s), 1 — N Таким образом, первым шагом было бы, имхо, выяснить, какой из них является «родительским», чтобы знать, как и где создавать правильные ассоциации. Однако это коренным образом изменит способ формирования вашего reponse json, поскольку между некоторыми объектами нет прямых связей (например, Religion не имеет прямого отношения к Major каким-либо образом, кроме как через Student -> Enrollment -> then Major ). Таким образом, ответ будет примерно таким: религии [i].студенты [i].зачисление [i].специальности [i]. И в этом случае прямая сортировка Major s по порядку Religion s была бы тем, что вы сделали бы после получения всех религий и их вложенных объектов, сопоставления Major s по Student s и сортировки их по своему усмотрению. Насколько я знаю, нет ни одного запроса (или комбинации вложенных запросов), который мог бы сделать это за вас в базе данных SQL без прямого (или даже косвенного) предупреждения о спойлере внешнего ключа, отсюда и возникает ошибка sequelize.

Однако есть способ

. Барабанная дробь, пожалуйста … «Сквозные» таблицы. т. е. промежуточные / соединительные таблицы, которые действуют как реляционные таблицы между моделями. Хотя обычно они используются для N — N отношений, их также можно использовать в подобных ситуациях для создания ассоциаций, в которых ранее их не могло быть, путем создания таблиц соединений. Однако стоит отметить сложности, связанные с использованием сквозных / соединительных таблиц — см. Документацию по этому вопросу Здесь

В целом, я думаю, что наиболее эффективным способом моделирования базы данных было бы что-то вроде этого

Изображение, показывающее дизайн БД

Итак, как бы мы это сделали? Нам нужно создать «сквозные» модели для зачисления на основной, основной для посещаемости и религии для основного. *** Обновление *** «Сквозные» модели будут выглядеть примерно так:

ReligionMajors

 const { Model, Sequelize, DataTypes } = require('sequelize');
const db = require('../path/to/db.js');
// import any models to associate
const Religion = require('./Religion');
const Major = require('./Major');

class ReligionMajors extends Model {}
ReligionMajors.init({
  religionId: {
    type: DataTypes.INTEGER,
    references: {  // You don't need to include this, just showing for reference 
      model: Religion,
      key: 'id'
    }
  },
  majorId: {
    type: DataTypes.INTEGER,
    references: {  // again you don't need this, just showing for reference
      model: Major,
      key: 'id'
    }
  }
});
Religion.belongsToMany(Major, { through: ReligionMajors });
Major.belongsToMany(Religion, { through: ReligionMajors});
 

EnrollmentMajors

 const { Model, Sequelize, DataTypes } = require('sequelize');
const db = require('../path/to/db.js');
// import any models to associate
const Enrollment = require('./Enrollment');
const Major = require('./Major');

class EnrollmentMajors extends Model {}
EnrollmentMajors.init({
  enrolmentId: {
    type: DataTypes.INTEGER,
  },
  majorId: {
    type: DataTypes.INTEGER,
  }
});
Enrollment.belongsToMany(Major, { through: EnrollmentMajors });
Major.belongsToMany(Enrollment, { through: EnrollmentMajors});

 

AttendanceYearMajors

 const { Model, Sequelize, DataTypes } = require('sequelize');
const db = require('../path/to/db.js');
// import any models to associate
const AttendanceYear = require('./AttendanceYear');
const Major = require('./Major');

class AttendanceYearMajors extends Model {}
AttendanceYearMajors.init({
  attendanceYearId: {
    type: DataTypes.INTEGER,
  },
  majorId: {
    type: DataTypes.INTEGER,
  }
});
AttendanceYear.belongsToMany(Major, { through: AttendanceYearMajors });
Major.belongsToMany(AttendanceYear, { through: AttendanceYearMajors});
 

Сложная часть этого заключается в том, что вам, возможно, придется начать думать о том, когда и как вы хотите создать эти ассоциации в записи. Кроме того, это изменяет отношения между моделями Major и Enrollments на отношения «многие ко многим», но это нормально.

Что мы можем теперь сделать, как я уже говорил ранее, это выяснить, когда и как создавать записи в моделях «через», чтобы создать нужные нам ассоциации.

Один из способов выполнить Religion Major ассоциацию to — это, в основном, выполнить ряд шагов с имеющимися у вас данными, т.е.

 const db = require('../path/to/db.js');
const Enrollment = require('../path/to/model/Enrollment');
const Major = require('../path/to/model/Major');
const Student = require('../path/to/model/Student');
const Religion = require('../path/to/model/Religion');
const EnrollmentMajors = require('../path/to/model/EnrollmentMajors');
const ReligionMajors = require('../path/to/model/ReligionMajors');
try{
   const studentEnrollment = await Enrollment.create(
      {
         studentId: studentIdFromReq,
         attendanceYearId: attendanceYearIdFromRequest,
      }
   );
   if(studenEnrollment){
      // associate the Enrollment with the Major if you have the Major id
      const studentEnrolledMajor = await EnrollmentMajors.create(
         {
             enrollmentId: studentEnrollment.id,
             majorId: majorId
         }
      )
      // Then, get the students' Religion Id, and associate with Major
      const studentWithReligion = await Student.findByPK(studentIdFromReq, 
         {include: [Religion]}
      )
      const religionMajorAssociation = await ReligionMajors.findOrCreate( 
         {
            religionId: studentWithReligion.religion.id, // or student.religionId
            majorId: majorId
         }
      )
      /* we use findOrCreate to avoid making duplicate religion-major assocs */
      if(religionMajorAssociation){
         // The association was created successfully, so you can do whatever else
      }
   }
} catch(err){
   console.log(err)
}
 

Обратите внимание, что я помещаю код в блок try-catch. В целом это хорошая практика, поэтому вы можете легко увидеть, какие ошибки может вызвать sequelize (если таковые имеются)…

Комментарии:

1. Спасибо миллион. Я действительно ценю ваши усилия. Я этого не ожидал. Вы даете мне много ценной информации. Спасибо. Я обновил вопрос и добавил, как я думаю, как я его реализовал.

Ответ №2:

Вам нужно определить ассоциацию в religions файле, подобном этому, рядом с Religion.hasMany(models.Student ассоциацией:

  Religion.hasMany(models.Major, {
        foreignKey: "religionId",
        as: "major",
      });
 

Комментарии:

1. В нем говорится: SequelizeDatabaseError: Unknown column 'major.religionId' in 'field list' .

2. Как вы создавали таблицы? Какое правильное поле внешнего ключа в major to religions ?

3. В основной таблице — majoi_id и name, а в таблице религий — religion_id и name. У меня нет других полей.

4. В этом случае вам нужно добавить недостающие столбцы и внешние ключи, чтобы связать все эти таблицы.

5. Это имеет смысл? Так и должно быть? Спасибо.