如何使用 Bookshelf.js 正确更新模型?

How do I properly update models with Bookshelf.js?

我确定我遗漏了什么,但我发现书架 API 一直让我感到困惑。这是我正在尝试做的事情:

特别是在这个例子中:

所以我在 Bookshelf 模型中有这样的东西,作为 class(即 "static")方法("info" 有字段 "serial","example1", 和 "example2"):

insertOrUpdate: function (info) {
    return new Radio({'serial':info.serial}).fetch().then(function (model) {
        if (model) {
            model.set('example1', info.example1);
            return model.save({}, {
                method: 'update',
                patch: true
            })
        } else {
            return new Radio({
                serial: info.serial,
                example1: info.example1,
                example2: info.example2
            }).save({}, {
                method: 'insert'
            })
        }
    }).then(function (model) {
        console.log("SUCCESS");
    }).catch(function (err) {
        console.log("ERROR", err);
    });
}

调用示例:

Radio.insertOrUpdate({
    serial: ...,
    example1: ...,
    example2: ...
})

我 运行 遇到的问题是,虽然 "insert" 案例有效,但 "update" 案例失败:

ERROR { Error: ER_PARSE_ERROR: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'where `serial` = '123223'' at line 1

打开 Knex 调试后很明显,生成的查询缺少 set 子句:

update `radios` set  where `serial` = ?

现在,我专注于 fetch and save 的 Bookshelf 文档,我想知道我是否走错了方向。

我知道我使用的 API 有误,但我想不通。为了让它进入半工作状态,我注意到/不得不做一些奇怪的事情:

无论如何,我该如何正确地做到这一点?如何使此插入和更新工作?

更重要的是,这在哪里记录?真诚地我假设它 在某个地方清楚地记录下来,我现在看不到树木的森林,所以我真的很感激在文档中遵循一些方向甚至不仅仅是直接回答,因为我确实需要弄清楚这一点。我在 Bookshelf 上花费了 很多 时间而不是实际开发,以至于我几乎希望我从一开始就坚持直接 SQL 查询。

经过几次反复的猜测,我似乎已经开始工作了,但我不知道这是否正确,或者我如何在不猜测的情况下确定这一点,我绝对不支持它的正确性。

基本上我可以通过将 "update" 案例修改为:

来让它工作
  • 将属性作为第一个参数传递给 save,而不是用 set 设置它们。

导致最终解决方案:

insertOrUpdate: function (info) {
    return new Radio({'serial':info.serial}).fetch().then(function (model) {
        if (model) {
            // pass params to save instead of set()
            var params = { 'example1' : info.example1 }
            return model.save(params, {
                method: 'update',
                patch: true
            })
        } else {
            return new Radio({
                serial: info.serial,
                example1: info.example1,
                example2: info.example2
            }).save({}, {
                method: 'insert'
            })
        }
    }).then(function (model) {
        console.log("SUCCESS");
    }).catch(function (err) {
        console.log("ERROR", err);
    });
}

我仍然不确定 how/if forge 是否适合这里,或者在 "insert" 情况下 save 的第一个参数应该如何处理。

更重要的是,我现在不完全确定 set 的用途。 ORM 框架的主要好处之一是 假设 使这种东西变得透明(即 "save" 可以正常工作,同时让您无需考虑就可以使用模型,并且你不必知道你 "save" 发生了什么变化——我 应该 能够提前得到未知的任意代码 set 东西能够在不知道发生了什么变化的情况下保存它,但看起来我不能),所以我不确定我从这里的书架上实际获得了什么。必须有更好的方法。

这很有趣,我花了一些时间才明白发生了什么。

您似乎已经发现,关于 patch 选项的 save() 方法 documentation 声明它

Only save attributes supplied in arguments to save.

所以您只需要将代码更改为

if (model) {
    model.set('example1', info.example1);
    return model.save();
}

并且 set 属性将被保存。

BUT BUT BUT BUT

ALL 属性将进入 update 语句,甚至 id!

这是 ORM 的常见行为,其基本原理是如果我们从一个事务中获取数据并从另一个事务中保存(糟糕,糟糕的做法!),数据可能已被其他客户端更改。所以只保存部分属性可能会导致状态不一致。

但是 patch 属性的存在本身就违反了这个概念。所以 Bookshelf 可以通过以下方式改进:

  • 只是弃用 patch 选项。 (我更喜欢)
  • 由于 Bookshelf 模型跟踪更改的属性,我认为在这方面使更新更智能应该是微不足道的。此更改也可能导致弃用 patch 选项。
  • 另一种方法可以使 patch 语义与更改的属性相关,而不是仅与 save() 上提供的属性相关。但不幸的是,这种变化可能会破坏一些用例。
  • 或最终引入一个 选项来对所有更改的属性进行操作。但这感觉很乱。