【问题标题】:Sequelize Associations: How to update parent model when creating child?Sequelize Associations:创建孩子时如何更新父模型?
【发布时间】:2021-10-02 13:41:56
【问题描述】:

看来我误解了 sequelize .hasMany().belongsTo() 关联以及如何在服务中使用它们。我有两个模型:

const User = db.sequelize.define("user", {
  uid: { /*...*/  },
  createdQuestions: {
    type: db.DataTypes.ARRAY(db.DataTypes.UUID),
    unique: true,
    allowNull: true,
  },
});
const Question = db.sequelize.define("question", {
  qid: { /*...*/  },
  uid: {
    type: db.DataTypes.TEXT,
  },
});

鉴于一个用户可以有很多问题并且每个问题只属于一个用户,我有以下关联:

User.hasMany(Question, {
    sourceKey: "createdQuestions", 
    foreignKey: "uid",
    constraints: false,
});
Question.belongsTo(User, { 
    foreignKey: "uid", 
    targetKey: "createdQuestions",
    constraints: false,
});

我想要实现的是:在创建问题对象后,qid 应该驻留在"createdQuestions" 下的用户对象中——就像uid 驻留在uid 下的问题对象中一样。我认为 sequelize 关联会为我做的是保存个人调用和更新用户对象。有没有对应的方法?到目前为止我所拥有的是:

const create_question = async (question_data) => {
  const question = { /*... question body containing uid and so forth*/ };

  return new Promise((resolve, rejected) => {
    Question.sync({ alter: true }).then(
      async () =>
        await db.sequelize
          .transaction(async (t) => {
            const created_question = await Question.create(question, {
              transaction: t,
            });
          })
          .then(() => resolve())
          .catch((e) => rejected(e))
    );
  });
};

然而,这只会创建一个问题对象,但不会更新用户。我在这里错过了什么?

【问题讨论】:

    标签: javascript node.js sequelize.js


    【解决方案1】:

    在 SQL 中建模一对多关系

    SQL 与 NoSQL

    在 SQL 中,与 NoSQL 中的情况相反,每个属性都有固定的数据类型和固定的位数限制。这在创建新表时的 SQL 命令中体现出来:

    CREATE TABLE teachers (
       name VARCHAR(32),
       department VARCHAR(64),
       age INTEGER
    );
    

    这背后的原因是允许我们通过知道每一行的长度来轻松地访问数据库中的任何属性。在我们的例子中,每一行都需要存储空间:

    32 字节 (姓名) + 64 字节 (部门) + 4 字节 (年龄) = 100 字节

    这是 关系数据库 中一个非常强大的功能,因为我们知道每条数据在记忆。

    一对多关系:案例研究

    现在,假设我们有这 3 个表

    假设我们想在 classesteachers 之间创建 一对多 关系,其中教师可以开设许多课程。

    我们可以这样想。但是,这个模型是不可能的,主要有两个原因:

    • 这将使我们失去恒定时间检索,因为我们不再知道列表的大小
    • 我们担心分配给列表属性的空间量不足以容纳未来的数据。假设我们分配了 10 节课所需的空间,最终我们得到了一位老师教授 11 节课。这将促使我们重新创建数据库以增加列大小。

    另一种方法是:

    虽然这种方法可以解决列大小有限的问题,但我们不再有单一事实来源。相同的数据会被多次复制和存储。

    这就是为什么对于这种一对多关系,我们需要将教师的 ID 存储在这个班级表中。

    这样,我们仍然可以通过运行找到老师可以教的所有课程

    SELECT *
    FROM classes
    WHERE teacherID = teacher_id
    

    我们将避免前面讨论的所有问题。

    【讨论】:

      【解决方案2】:

      您的关系是一对多关系。一个用户可以有多个问题。在 SQL 中,这种关系是通过向 Question 中添加一个名为 userId 或 Uid 的属性来建模的。在 Sequelize 中,这将通过 hasMany 或 BelongsTo 来实现,如下所示:

      User.hasMany(Question)
      Question.belongsTo(User, {
         foreignKey: 'userId',
         constraints: false
      })
      

      换句话说,我认为您不需要 User 下的 CreatedQuestions 属性。对 oneToMany 关系建模只需要一个外键。

      现在,在创建新问题时,您只需要以这种方式添加 userId

      createNewQuestion = async (userId, title, body) => {
        const question = await Question.create({
          userId: userId,  // or just userId
          title: title,  // or just title
          body: body  // or just body
        })
        return question
      }
      

      记住,我们不在 SQL 中存储数组。即使我们能找到一种方法来做到这一点,这也不是我们所需要的。总有更好的办法。

      【讨论】:

      • 谢谢!但是,我希望两个模型互相了解,这就是 createdQuestions 的意义所在。我——也许天真地——认为联想可以做到这一点。通过创建Question,这个Question 知道它属于哪个User并且User 也知道他或她拥有哪个QuestionsQuestionsbeeing 直接从获取的User 访问而无需任何额外的调用或过滤将非常方便,我希望通过建立关联一次性实现这一目标。
      • 在这种情况下,小心不要出现著名的错误cyclic dependency。看看这个stackoverflow.com/questions/35116541/…
      • 是的,我知道这个线程和问题,但这里constraints: false 已经设置好了。我不确定,我觉得应该没问题。但是,如果让两个模型相互了解通常是不好的做法,我会很感激你能给出一个可靠的解释,并将其作为一个答案。
      • 将为您提供@JulianFock :)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-08-26
      • 1970-01-01
      • 1970-01-01
      • 2016-11-25
      • 2014-01-11
      • 1970-01-01
      • 2014-01-05
      相关资源
      最近更新 更多