Ассоциация в Sequelize

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

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

Я знаю, как настроить базовый уровень ассоциаций, например. студент и майор. Но с моим знанием базы данных я понятия не имею, как ассоциироваться с religions и majors, а также с 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;
};

религии

"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;
};

студенты

'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;
};

и зачисления

'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;
};

Что я сделал и не работает

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,
                    },
                  },
                ],
              },
            ],
          },
        ],
      },
    ],
  });

Ошибка

SequelizeEagerLoadingError: Major is not associated to Religion!

Обновлено

У меня есть файлы следующих моделей (которые будут таблицами в базе данных) в этой структуре src/database/models/:

  1. майоры
  2. посещаемость_лет
  3. религии
  4. студенты
  5. регистрации

Вся структура:

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.info('Connected to the database.');
  })
  .catch((error) => {
    logger.error('Unable to connect to the database.', error);
    console.info(`Unable to connect to the database.`, error);
    process.exit(1);
  });

fs.readdirSync(__dirname)
  .filter((file) => {
    return (
      file.indexOf('.') !== 0 && file !== basename && 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, заключается в том, что я боюсь случайно потерять свои данные в продакшене, если буду обновлять модели или добавлять новые. Поэтому я использовал equalize-cli . С его помощью я могу превратить свои модели в таблицы, запустив sequelize db:migrate.

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

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

Amadou Beye 20.12.2020 14:57

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

Htet Phyo Naing 20.12.2020 15:40
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
1
2
2 014
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

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

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

Там написано: SequelizeDatabaseError: Unknown column 'major.religionId' in 'field list'.

Htet Phyo Naing 20.12.2020 20:47

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

Anatoly 20.12.2020 21:32

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

Htet Phyo Naing 20.12.2020 21:45

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

Anatoly 20.12.2020 21:48

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

Htet Phyo Naing 20.12.2020 21:50

Ну, вы не создаете таблицы, используя sync метод Sequelize, и вручную создаете все таблицы, тогда да. В противном случае все записи в этих таблицах вообще были бы связаны.

Anatoly 20.12.2020 21:53
Ответ принят как подходящий

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

Сначала несколько советов из личного опыта.

  1. Sequelize очень мощный. И под капотом это решает для вас множество проблем, а это означает, что вам не нужно беспокоиться о многих вещах, таких как атрибуты первичного ключа, имена столбцов внешнего ключа, имена таблиц и т. д. Вне некоторых действительно сложных ассоциаций или в некоторых краях случаях (например, если вы работаете с устаревшей базой данных), вам действительно не нужно явно объявлять, что это такое, а лучше позволить секвенсору сделать всю тяжелую работу за вас. Я упоминаю об этом, потому что заметил, что вы пытались указать атрибуты primaryKey и включили атрибут tableName в объект параметров определений вашей модели, что нормально, но на самом деле не нужно, и на самом деле может фактически мешать запросам движков продолжения, и в этом случае вы можете должны определять эти атрибуты везде, и это просто беспорядок. Sequelize генерирует атрибуты primaryKey и tableName по умолчанию — поэтому, если можете, максимально уменьшите количество ненужных определений — Узнайте, почему, из документации по выводу имен таблиц здесь. Если вы чувствуете необходимость иметь свой собственный ключ для моделей, рассмотрите возможность использования атрибута 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. Я также заметил, что вы пытались определить ассоциации моделей в статических методах в определениях моделей. Я не уверен, почему вы это сделали, но я не думаю, что ассоциации определяются именно так, как исходя из моего опыта, так и из официальной документации по продолжению (версия 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;

Также стоит иметь в виду, что когда вы связываете объекты модели, sequenceize будет автоматически множить имя объекта в результатах в зависимости от отношения (т. е. если родительский объект имеет много дочернего объекта) и возвращает результаты как множество. например если Religion.hasMany(Major), результат вернет religion.majors = [/*an array of majors*/].

  1. Из приведенного выше примера вы можете начать видеть некоторые изменения, которые могут сразу решить вашу проблему. Но последнее, о чем я хочу упомянуть, прежде чем предлагать свое решение. Опять же, не без связи с предыдущим пунктом, я заметил, что вы пытались указать ссылки на некоторые атрибуты. Вам не нужно этого делать. Это что-то вроде NoSQL. Достаточно определить только атрибут и его тип, и когда ассоциация будет создана, секвенсор вычислит внешний ключ. Вы также можете указать другие детали в ассоциации. Так, например; Предполагая отношение 1 - N между моделями Religion и Major -

в файле модели Major.js вы можете указать религию FK следующим образом

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;

Тогда в 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;

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

Что, как говорится. Теперь к вашему вопросу: (возможное решение 1 - простой способ)

Самое первое, что вам нужно сделать, это точно определить, на что будет похожа структура отношений между сущностями. Из объекта data мне кажется, что-то вроде

  • От религии к основной: от 1 до N (одна религия имеет много основных направлений)
  • Major to AttendanceYear: от 1 до N (один Major имеет много лет посещаемости)
  • AttendanceYear to Student: от 1 до N (в одном году посещаемости много учеников) таким образом, я представляю, что ваш желаемый ответ на продолжение будет примерно таким:
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. сохраните экземпляр подключения к базе данных sequenceize в отдельном файле. Я звоню 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) намного лучше для этого, и вы также можете передать включения и другие параметры и т. д. При этом, надеюсь, это проливает свет на то, как работает секвенирование и как вы можете подойти к проблеме; Тем не менее, у меня такое ощущение, что это не то, к чему вы стремитесь, а если нет, то это, по крайней мере, закладывает основу для второго решения, которое я предлагаю.

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

The 2nd solution, in my opinion, is one that I believe more closely addresses the problem. from your model definitions (and giving it a little thought) it appears that it should be -
  • у каждого Major много Enrollment и наоборот, N - N (поскольку у студента может быть несколько специальностей в одном наборе)
  • каждый AttendanceYear имеет много Enrollments, 1 - N
  • каждый Religion имеет много Students, 1 - N,
  • у каждого Student может быть много Enrollment (и, соответственно, Major), 1 - N Таким образом, первым шагом было бы, имхо, выяснить, что является «родителем» для чего, чтобы узнать, как и где создавать правильные ассоциации. Однако это коренным образом изменит способ формирования вашего json-ответа, поскольку между некоторыми сущностями нет прямых отношений (например, Religion никак напрямую не связан с Major, кроме как через Student -> Enrollment -> затем Major). Таким образом, ответом будет что-то вроде религий[i].students[i].enrollments[i].majors[i]. И в этом случае прямая сортировка Major в порядке Religion будет чем-то, что вы должны сделать после получения всех религий и их вложенных объектов, сопоставления Major с Student и сортировки их, как вы хотите. Насколько я знаю, нет единого запроса (или комбинации вложенных запросов), который мог бы сделать это для вас в базе данных SQL без прямого (или даже косвенного) внешнего ключа. Оповещение о спойлере, вот где появляется ошибка продолжения от.

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

. Drum roll please... "Through" Tables. i.e. intermediate/junction tables that act as relational tables between Models. Though generally used for N - N relationships, they can also be used in situations like this to create associations where none might have previously existed, by creating Junction tables. Worth noting however, are the intricacies that come with using through/junction tables - See the docs on that here

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

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

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

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 состоит в том, чтобы, в основном, выполнить ряд шагов с данными, которые у вас есть, т.е.

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.info(err)
}

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

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

Htet Phyo Naing 21.12.2020 18:31

Другие вопросы по теме