简体   繁体   English

Sequelize 中的关联

[英]Association in Sequelize

I'm trying to get data like below:我正在尝试获取如下数据:

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

I know how to set up a basic level of associations for eg.我知道如何为例如建立基本级别的关联。 student and major.学生和专业。 But in my database knowledge, I have no idea how to associate with religions and majors , and also in Sequelize .但在我的数据库知识中,我不知道如何与religionsmajors相关联,也不知道在Sequelize中。 Please help.请帮忙。

I have the followings tables:我有以下表格:

  1. majors专业
  2. attendance_years出勤年数
  3. religions宗教
  4. students学生
  5. enrollments入学人数

Below is my models.下面是我的模型。

majors专业

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

attendance_years出勤年数

"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/ :我在这个结构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

I have an index.js file inside that models/ directory and it is like below:我在该models/目录中有一个index.js文件,如下所示:

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

With that implementation, I don't need to import the required models inside the model files and also route handlers and just need the following line.通过该实现,我不需要在 model 文件和路由处理程序中导入所需的模型,只需要以下行。

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

And the reason that I don't use the sync approach is that I'm afraid to accidentally lose my data in the production if I update the models or add a new one.而我不使用sync方法的原因是,如果我更新模型或添加新模型,我害怕在生产中意外丢失我的数据。 Therefore, I used sequelize-cli .因此,我使用sequelize-cli With it, I can turn my models into tables by running sequelize db:migrate .有了它,我可以通过运行sequelize db:migrate将我的模型变成表格。

The reason that I have explicitly defined the attribute and table name is that I want them to follow the MySQL naming conventions: attendance_years and attendance_year_id for instance.我明确定义属性和表名的原因是我希望它们遵循 MySQL 命名约定:例如, attendance_years _年和attendance_year_id _年_id。 But when I run the calls to the database, I see lots of naming aliases in the terminal: attendance_year_id as attendanceYearId etc. I think this might impact the querying performance and so, I will consider letting sequelize completely manage the naming conventions.但是当我运行对数据库的调用时,我在终端中看到很多命名别名:出席_year_id 和出席年 ID 等。我认为这可能会影响查询性能,因此,我会考虑让 sequelize 完全管理命名约定。

You need to define an association in religions file like this next to the Religion.hasMany(models.Student association:您需要在Religion.hasMany(models.Student religions

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

Thanks for reaching out to me on Twitter.感谢您通过 Twitter 与我联系。 I really appreciate it.对此,我真的非常感激。 That being said, let's see if we can go about answering your question.话虽如此,让我们看看我们是否可以回答您的问题。 Please allow me to geek out a bit before getting to the solution I wish to provide.在找到我希望提供的解决方案之前,请允许我先了解一下。

First, some advice from my personal experience.首先,从我个人的经验中得到一些建议。

  1. Sequelize is very powerful . Sequelize非常强大 And under the hood it solves a lot of problems for you, meaning you don't have to worry about a lot of things like Primary key attributes, Foreign key column names, table names etc. Outside of some really complex associations or in some edge cases (like if you're working with a legacy database, for instance), you really don't have to explicitly declare what those are, but rather let sequelize do the heavy lifting for you.在底层它为你解决了很多问题,这意味着你不必担心很多事情,比如主键属性、外键列名、表名等。在一些非常复杂的关联之外或在某些边缘案例(例如,如果您使用的是遗留数据库),您实际上不必明确声明它们是什么,而是让 sequelize 为您完成繁重的工作。 I mention this because I noticed you tried specifing primaryKey attributes and included the tableName attribute in the options object of your model definitions, which is okay, but really unnecessary, and could in fact actually interfere with the sequelize engines' queries in which case you may have to define these attributes everywhere and that's just a mess.我提到这一点是因为我注意到您尝试primaryKey属性并将tableName属性包含在 model 定义的选项 object 中,这没关系,但实际上没有必要,实际上可能会干扰 sequelize 引擎的查询,在这种情况下您可能必须在任何地方定义这些属性,这只是一团糟。 Sequelize generates primaryKey attributes and tableName s by default - so, if you can, please minimize unnecessary definitions as much as possible - See why from the docs on table name inference here . Sequelize 默认生成primaryKey属性和tableName s - 因此,如果可以,请尽可能减少不必要的定义 - 请参阅此处有关表名推断的文档中的原因 If you do feel the need to have your own custom key for models, consider using a UUID attribute, like so.如果您确实需要为模型提供自己的自定义键,请考虑使用 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
}

That saves you both the trouble of having to uniquely name primaryKey fields, as well as preventing situations where keys may have similar values.这既省去了必须唯一命名 primaryKey 字段的麻烦,又避免了键可能具有相似值的情况。 It also gives you an extra unique attribute to use in your queries to ensure you get a single record.它还为您提供了一个额外的独特属性,可在查询中使用,以确保您获得一条记录。

  1. I also noticed you tried defining the model associations in static methods in the model definitions.我还注意到您尝试在 model 定义中的 static 方法中定义 model 关联。 I'm not sure why you did this, but I don't think that's how associations are defined, both from my experience and the official sequelize docs (Version 6 - as seen here ).我不确定你为什么这样做,但我不认为这是定义关联的方式,无论是根据我的经验还是官方的续集文档(版本 6 -如此处所示)。 Rather, how it's done is that associations are defined in the model file after the model has been initialized, before exporting - eg相反,它的完成方式是在 model 初始化之后,在导出之前model 文件中定义关联 - 例如
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 (ie if the parent entity hasMany of the child entity), and returns the results as an array.另外值得记住的是,当你关联 model 实体时,sequelize 会自动将结果中的实体名称复数,具体取决于关系(即如果父实体有许多子实体),并将结果作为大批。 eg if Religion.hasMany(Major) , the result will return religion.majors = [/*an array of majors*/] .例如,如果Religion.hasMany(Major) ,结果将返回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.这是一种 NoSQL 的事情。 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.只定义属性和类型就足够了,当建立关联时,sequelize 会找出外键。 You can also specify other details in the association.您还可以在关联中指定其他详细信息。 So for instance;例如, Assuming a 1 - N relationship between Religion and Major models -假设ReligionMajor模型之间存在 1 - N 的关系 -

in Major.js model file you can specify the religion FK like thisMajor.js model 文件中,您可以像这样指定宗教 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;

Then in Religion.js然后在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.作为旁注,您通常不必在关联中包含onDeleteonUpdate属性,因为默认值非常适合大多数用例。 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)现在回答您的问题:(可能的解决方案 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 likedata object 来看,在我看来是这样的

  • Religion to Major: 1 to N (One religion has Many Majors)宗教到专业:1到N(一个宗教有很多专业)
  • Major to AttendanceYear: 1 to N (One Major has Many attendance years)专业到出勤年:1 到 N(一个专业有很多出勤年)
  • AttendanceYear to Student: 1 to N (One Attendance Year has many students) thus, I imagine your desired sequelize response to be something like this:学生的出勤年: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*/}
]

Okay.好的。 I'm not sure if this is what you're going for, but going off of the example you gave, that's what I'm working with.我不确定这是否是你想要的,但离开你给出的例子,这就是我正在使用的。 For the code, first, some configurations that will help you down the line对于代码,首先,一些可以帮助您下线的配置

  1. save the sequelize db connection instance in a seperate file.将 sequelize db 连接实例保存在单独的文件中。 I'm calling it db.js我称它为db.js
const { Sequelize } = require('sequelize');

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

I'm putting this here now, just so it's clear what I'm referring to when using the db variable elsewhere.我现在把它放在这里,只是为了清楚我在别处使用db变量时所指的内容。 Then we create the models Religion.js然后我们创建模型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;

And with that, all your entities are setup to get the data in the that shape you requested.这样一来,您的所有实体都已设置为以您请求的形状获取数据。 eg (using in a function)例如(在函数中使用)

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

Worth noting, if you're going to search for something using it's primary key, then the .findByPK(idValueOrVariable) is much better for that, and you can also pass in includes and other options etc. That being said, Hopefully this sheds some light on how sequelize works and how you can approach the problem;值得注意的是,如果您要使用它的主键搜索某些内容,那么.findByPK(idValueOrVariable)会更好,您还可以传入包含和其他选项等。话虽如此,希望这能减少一些了解 sequelize 的工作原理以及如何解决问题; However, I get the feeling that this isn't what you're going for, and if not, then this at least lays the ground work for the 2nd solution I'm proposing.但是,我觉得这不是您想要的,如果不是,那么这至少为我提出的第二个解决方案奠定了基础。

Possible Solution 2: Restructuring可能的解决方案 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 - 从您的 model 定义(并稍微考虑一下)看来,它应该是 -
  • each Major has many Enrollment s and vice versa, N - N (because a student may have multiple majors in the same enrollment)每个Major有很多Enrollment ,反之亦然,N - N (因为一个学生可能在同一个招生中有多个专业)
  • each AttendanceYear has many Enrollment s, 1 - N每个AttendanceYear有很多Enrollment , 1 - N
  • each Religion has many Student s, 1 - N,每个Religion有很多Student ,1 - N,
  • each Student can have many Enrollment s (and by extension, Major s), 1 - N Thus first step would then be, imho, figuring out which is 'parent' to which, to know how and where to make the right associations.每个Student可以有许多Enrollment s(以及扩展的Major s),1 - N 因此第一步将是,恕我直言,找出哪个是“父母”,以了解如何以及在哪里进行正确的关联。 However, this will fundamentally change the way your reponse json will be shaped, seeing as there's no direct relationships between some entities (for instance, Religion is not directly related to Major in any way except through Student -> Enrollment -> then Major ).但是,这将从根本上改变您的响应 json 的形成方式,因为某些实体之间没有直接关系(例如,除了通过Student -> Enrollment -> 然后Major之外, ReligionMajor没有任何直接关系)。 So the response would be something like religions[i].students[i].enrollments[i].majors[i].因此响应将类似于religions[i].students[i].enrollments[i].majors[i]。 And in that case, to directly sort Major s in order of Religion s would be something you would do after getting all the religions and their nested objects, and mapping Major s by Student s and sorting them however you want.在这种情况下,直接按Religion的顺序对Major进行排序将是您获取所有宗教及其嵌套对象,然后按Student映射Major并根据需要对它们进行排序之后要做的事情。 As far as I know, there's no single query (or combination of nested queries) that can do this for you in an SQL db without a direct (or even an indirect) Foreign Key - Spoiler alert, this is where the sequelize error is coming from.据我所知,在没有直接(甚至间接)外键的情况下,在 SQL 数据库中没有单个查询(或嵌套查询的组合)可以为您执行此操作 - 剧透警报,这就是续集错误即将到来的地方从。

However, there is a way不过,有办法

. . Drum roll please... "Through" Tables . 请打鼓…… “通过”桌子 ie 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. 虽然它们通常用于 N - N 关系,但它们也可以在这种情况下使用,通过创建连接表来创建以前可能不存在的关联。 Worth noting however, are the intricacies that come with using through/junction tables - See the docs on that here 然而,值得注意的是,使用直通/联结表带来的复杂性 - 请参阅此处的文档

Overall, I think the most effective way to model the database would be something like this总的来说,我认为 model 数据库的最有效方法是这样的

Image Showing DB Design显示数据库设计的图像

So, how would we do this?那么,我们将如何做到这一点? We need to create "through" models for enrollment to major, major to attendance, and religion to major.建立专业招生、专业到出勤、宗教到专业的“贯通”模式。 *** Update *** The "through" models will looks something like this: *** 更新 *** “通过”模型看起来像这样:

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

The tricky part with this is that you may have to start thinking about when and how you want to make these associations on a record.棘手的部分是您可能必须开始考虑何时以及如何将这些关联记录在案。 Also, this changes the relationship between the Major and Enrollments models to a many to many relationship, but that's okay.此外,这会将MajorEnrollments模型之间的关系更改为多对多关系,但这没关系。

What we can now do, like I said before, is figure out when and how to create records in the 'through' models to create the associations we need.正如我之前所说,我们现在可以做的是弄清楚何时以及如何在“通过”模型中创建记录以创建我们需要的关联。

One way to do the Religion to Major association would be to, basically perform a series of steps with the data you have ie进行ReligionMajor关联的一种方法是,基本上使用您拥有的数据执行一系列步骤,即

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) }

Notice I put the code in a try-catch block.请注意,我将代码放在 try-catch 块中。 This is good practice generally, so you can easily see whatever errors sequelize might throw (if any)...这通常是一种很好的做法,因此您可以轻松查看 sequelize 可能引发的任何错误(如果有的话)......

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM