如何使用 Bookshelf.js 正确更新模型?
How do I properly update models with Bookshelf.js?
我确定我遗漏了什么,但我发现书架 API 一直让我感到困惑。这是我正在尝试做的事情:
- 我有一个名为
Radio
的模型,它有一个名为 serial
的应用程序分配的字符串主键,并且为了本示例的目的,有两个名为 example1
和 [=18 的字段=].我在模型定义中用 idAttribute: 'serial'
指定了自定义 ID。
- 我正在尝试使用 Bookshelf 执行更新插入(不是直接使用 Knex,在我的实际应用程序中查询变得相当复杂)。
- 我已经让 insert 案例正常工作,但似乎无法让 update 案例正常工作。
- 为简单起见,我现在不关心事务或原子性。我很满意得到一个简单的 select → insert/update 来工作。
特别是在这个例子中:
- 在插入集
example1
和 example2
上。
- 更新集
example1
并保持 example2
不变。
所以我在 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 有误,但我想不通。为了让它进入半工作状态,我注意到/不得不做一些奇怪的事情:
save
第一个参数没看懂。如果保存是 Model
的 static 方法,它 对我来说 有意义,但事实并非如此。这是一个实例方法,您已经可以将属性传递给 Model
构造函数(例如 new X(a:1).save({a:2})
意味着什么......?),并且您已经可以在保存之前使用 set
设置属性.所以我无法理解这一点。我必须将 {}
作为占位符传递,以便我指定选项。
有这个 forge
东西,但我不确定它的目的是什么,因为你已经可以将属性传递给 Model
构造函数(除非作者发现了一些好处X.forge({a:1})
对比 new X({a:1})
...?).
我发现我必须明确指定保存方法,因为 Bookshelf 有一个明显的怪癖:Bookshelf 根据 isNew()
选择方法,但 isNew()
总是true
当您将 id 传递给模型构造函数时,您必须在应用程序分配的 ID 情况下执行此操作。因此,对于应用程序分配的 ID,Bookshelf 将总是 执行"insert",因为它总是 认为模型"is new"。所以你必须强制方法 "update"...这只会增加我的书架混乱。
无论如何,我该如何正确地做到这一点?如何使此插入和更新工作?
更重要的是,这在哪里记录?真诚地我假设它 是 在某个地方清楚地记录下来,我现在看不到树木的森林,所以我真的很感激在文档中遵循一些方向甚至不仅仅是直接回答,因为我确实需要弄清楚这一点。我在 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()
上提供的属性相关。但不幸的是,这种变化可能会破坏一些用例。
- 或最终引入一个 新 选项来对所有更改的属性进行操作。但这感觉很乱。
我确定我遗漏了什么,但我发现书架 API 一直让我感到困惑。这是我正在尝试做的事情:
- 我有一个名为
Radio
的模型,它有一个名为serial
的应用程序分配的字符串主键,并且为了本示例的目的,有两个名为example1
和 [=18 的字段=].我在模型定义中用idAttribute: 'serial'
指定了自定义 ID。 - 我正在尝试使用 Bookshelf 执行更新插入(不是直接使用 Knex,在我的实际应用程序中查询变得相当复杂)。
- 我已经让 insert 案例正常工作,但似乎无法让 update 案例正常工作。
- 为简单起见,我现在不关心事务或原子性。我很满意得到一个简单的 select → insert/update 来工作。
特别是在这个例子中:
- 在插入集
example1
和example2
上。 - 更新集
example1
并保持example2
不变。
所以我在 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 有误,但我想不通。为了让它进入半工作状态,我注意到/不得不做一些奇怪的事情:
save
第一个参数没看懂。如果保存是Model
的 static 方法,它 对我来说 有意义,但事实并非如此。这是一个实例方法,您已经可以将属性传递给Model
构造函数(例如new X(a:1).save({a:2})
意味着什么......?),并且您已经可以在保存之前使用set
设置属性.所以我无法理解这一点。我必须将{}
作为占位符传递,以便我指定选项。有这个
forge
东西,但我不确定它的目的是什么,因为你已经可以将属性传递给Model
构造函数(除非作者发现了一些好处X.forge({a:1})
对比new X({a:1})
...?).我发现我必须明确指定保存方法,因为 Bookshelf 有一个明显的怪癖:Bookshelf 根据
isNew()
选择方法,但isNew()
总是true
当您将 id 传递给模型构造函数时,您必须在应用程序分配的 ID 情况下执行此操作。因此,对于应用程序分配的 ID,Bookshelf 将总是 执行"insert",因为它总是 认为模型"is new"。所以你必须强制方法 "update"...这只会增加我的书架混乱。
无论如何,我该如何正确地做到这一点?如何使此插入和更新工作?
更重要的是,这在哪里记录?真诚地我假设它 是 在某个地方清楚地记录下来,我现在看不到树木的森林,所以我真的很感激在文档中遵循一些方向甚至不仅仅是直接回答,因为我确实需要弄清楚这一点。我在 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()
上提供的属性相关。但不幸的是,这种变化可能会破坏一些用例。 - 或最终引入一个 新 选项来对所有更改的属性进行操作。但这感觉很乱。