关于Sequelize创建关联后注入的方法

99 阅读4分钟

添加到实例的特殊方法/mixins

当在两个模型之间创建关联时,这些模型将会被注入特殊的方法来对与其关联的模型进行交互。这些方法主要也是进行增删改查,具体会注入哪些方法由关联方式而定

一对一

现在又两个模型ClassModel表示班级,StudentModel表示学生

ClassModel.hasOne(StudentModel)

一对一,假设一个班级只有一个学生

  • classModelInstance.getStudent():获取此数据关联的所有数据
  • classModelInstance.setStudent():为已创建的数据设置关联
  • classModelInstance.createStudent():为此数据创建关联数据

各方法解释

  • 使用标准方式为两个模型创建数据

    // 创建一个班级
    const classData = await classModel.create({ name: "班级一" });
    // 创建一个学生
    const studentData = await studentModel.create({ name: "学生一", age: "18" });
    ​
    // 通过关联后混入的方法,得到该条数据关联的学生
    console.log(await classData.getStudent());  // null,因为此数据未关联任何学生
    
  • 使用 set 设置关联

    const classData = await classModel.create({ name: "班级一" });
    const studentData = await studentModel.create({ name: "学生一", age: "18" });
    ​
    // 设置关联
    await classData.setStudent(studentData);
    ​
    console.log(await classData.getStudent());  // 学生一
    
  • 使用 create 直接创建关联数据

    const classData = await classModel.create({ name: "班级一" });
    await classData.createStudent({ name: "学生一", age: "18" });
    ​
    console.log(await classData.getStudent());  // 学生一
    
  • 清空关联

    // 传入null值即可清空其关联
    classData.setStudent(null);
    
studentModel.belongsTo(classModel);

一个学生只能属于一个班级,所以也是一对一关系,拥有以上一致方法

  • studentModelInstance.getClass()
  • studentModelInstance.setClass(()
  • studentModelInstance.createClass(()

一对多

classModel.hasMany(studentModel);   // 一个班级有多个学生
studentModel.belongsTo(classModel); // 一个学生只能属于一个班级

关联后会混入以下方法

  • classModelInstance.getStudents():获取该班级下所有学生
  • classModelInstance.countStudents():获取该班级下的学生总数
  • classModelInstance.hasStudent():该班级下是否有这个学生
  • classModelInstance.hasStudents():该班级下是否有这些学生
  • classModelInstance.setStudents(Model | string | [Model] | string[])
  • classModelInstance.addStudent(Model | string | [Model] | string[]))`:将已有的学生添加进该班级
  • classModelInstance.addStudents(Model | string | [Model] | string[]))`:将已有的学生们添加进该班级
  • classModelInstance.removeStudent(Model | string | [Model] | string[]))`:删除这个学生
  • classModelInstance.removeStudents(Model | string | [Model] | string[]))`:删除这些学生
  • classModelInstance.createStudent(data):创建一个学生

方法解释

  • 为班级添加学生

    // 创建了一个班级
    const classData = await classModel.create({ name: "班级一" });
    ​
    // 创建了两个学生,此时这两个学生不属于任何班级
    const studentData1 = await studentModel.create({ name: "学生一", age: 18 });
    const studentData2 = await studentModel.create({ name: "学生二", age: 17 });
    ​
    // 为班级添加这两个学生
    await classData.addStudent(studentData1);
    await classData.addStudent(studentData2);
    ​
    console.log(await classData.getStudents()); // 学生一,学生二
    
    // 上面添加学生的方法也可以使用复数形式的方法,传递一个数组
    await classData.addStudents([studentData1, studentData2]);
    ​
    // 其实 addStudent 与 addStudents 是一致的,但从语义上来说还是做一下区分
    
  • 从班级里删除学生

    const classData = await classModel.create({ name: "班级一" });
    const studentData1 = await studentModel.create({ name: "学生一", age: 18 });
    const studentData2 = await studentModel.create({ name: "学生二", age: 17 });
    ​
    await classData.addStudents([studentData1, studentData2]);
    await classData.removeStudent(studentData1)
    ​
    console.log(await classData.getStudents());
    
    // remove 与 add 传递的参数一致,只不过意思相反
    // 同样也可使用 removeStudents 删除多个学生
    
  • 使用 create 直接创建学生,该方法一次只能创建一条数据

    const classData = await classModel.create({ name: "班级一" });
    ​
    await classData.createStudent({ name: "学生一", age: "18" });
    console.log(await classData.getStudents()); // 学生一
    
  • 清除关联数据,只是清空关联,并不是删除数据

    await classData.setStudents([]);
    

getter方法接受选项,就像通常的查找器方法一样(比如findAll):

const easyTasks = await project.getTasks({
  where: {
    difficulty: {
      [Op.lte]: 5,
    },
  },
});
const taskTitles = (
  await project.getTasks({
    attributes: ['title'],
    raw: true,
  })
).map(task => task.title);

多对多

多个老师可以教多个班级,多个班级也可以有多个老师

Foo.belongsToMany(Bar, { through: Baz })

多对多,一个Foo属于有个Bar

The same ones from Foo.hasMany(Bar):

  • fooInstance.getBars()
  • fooInstance.countBars()
  • fooInstance.hasBar()
  • fooInstance.hasBars()
  • fooInstance.setBars()
  • fooInstance.addBar()
  • fooInstance.addBars()
  • fooInstance.removeBar()
  • fooInstance.removeBars()
  • fooInstance.createBar()

对于belongsToMany关系,默认情况下getBars()将返回连接表中的所有字段。请注意,任何include选项都将应用于目标Bar对象,因此不可能像使用find方法快速加载时那样尝试为连接表设置选项。为了选择要包含的连接表的属性,getBars()支持一个joinTableAttributes选项,该选项的使用方法与setting through类似。包含中的属性。作为一个例子,给定Foo belongsToMany Bar,下面将输出没有连接表字段的结果:

const foo = Foo.findByPk(id, {
  include: [
    {
      model: Bar,
      through: { attributes: [] },
    },
  ],
});
console.log(foo.bars);
​
const foo = Foo.findByPk(id);
console.log(foo.getBars({ joinTableAttributes: [] }));

方法名称

如上例所示,Sequelize给这些特殊方法的名称是由一个前缀(例如get, add, set)与模型名称(第一个字母是大写的)连接而成的。必要时,使用复数形式,例如在fooInstance.setBars()中。同样,不规则复数也由Sequelize自动处理。例如,Person变成People, Hypothesis变成Hypothesis。

如果定义了别名,将使用它代替模型名来形成方法名。例如:

Task.hasOne(User, { as: 'Author' });
  • taskInstance.getAuthor()
  • taskInstance.setAuthor()
  • taskInstance.createAuthor()