web-dev-qa-db-ja.com

Bookshelf.jsでモデルを適切に更新するにはどうすればよいですか?

私は何かが欠けていると確信していますが、Bookshelf APIは執拗に混乱します。これが私がやろうとしていることです:

  • Radioという名前のアプリケーション割り当ての文字列主キーと、この例の目的のために、_example1_および_example2_という名前の2つのフィールドを持つserialというモデルがあります。モデル定義でカスタムIDを_idAttribute: 'serial'_で指定しました。
  • Bookshelfで(Knexを直接ではなく、実際のアプリケーションではクエリがかなり複雑になる)アップサートを実行しようとしています。
  • insertのケースは機能していますが、updateのケースは機能していないようです。
  • 簡単にするために、私は現在トランザクションや原子性について気にしていません。単純なselect→insert/updateを動作させることに満足しています。

そして特にこの例では:

  • 挿入時に_example1_および_example2_を設定します。
  • 更新時に_example1_を設定し、_example2_を変更せずにそのままにします。

したがって、Bookshelfモデルには、クラス(つまり「静的」)メソッド(「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: ...
})
_

ここで実行している問題は、「挿入」ケースが機能しているのに、「更新」ケースが失敗して次のようになることです。

_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
_

これは、生成されたクエリにset句が欠落しているKnexデバッグがオンになっている場合に明らかです。

_update `radios` set  where `serial` = ?
_

今、私は fetchsave のBookshelfドキュメントに焦点を当てており、間違った方向に向かっているかどうか疑問に思っています。

私は間違ったAPIを使用していることを知っていますが、それを理解することができません。私が気付いた奇妙なことのいくつかは、それを半動作状態にするためだけに実行する必要がありました。

  • saveの最初のパラメータがわかりません。 saveがModelstaticメソッドだった場合、それはだろう意味がありますが、そうではありません。これはインスタンスメソッドであり、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はalwaysが「挿入」を実行しますalwaysがモデルを「新しい」と考えるためです。したがって、メソッドを強制的に「更新」する必要があります...これは、私のBookshelfの混乱に追加されます。

とにかく、どうすればこれを正しく行うことができますか?この挿入と更新を機能させるにはどうすればよいですか?

さらに重要なことに、これはどこに文書化されているのでしょうか?誠実に私はそれがどこかに明確に文書化されていると仮定します、そして今私は木の森を見ることができないだけなので、私はこれを理解する必要があるので、ドキュメントは直接的な回答以上のものです。私は実際の開発ではなく、本棚にたくさんの時間を費やしてきました。

13
Jason C

それは興味深いもので、何が起こっているのかを理解するのに少し時間がかかりました。

ご存知のように、patchオプションに関するsave() method documentation は、

保存する引数で指定された属性のみを保存します。

したがって、コードを次のように変更するだけです

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

set属性が保存されます。

[〜#〜] but [〜#〜][〜#〜] but [〜#〜][〜#〜] but [〜#〜][〜#〜] but [〜#〜]

[〜#〜] all [〜#〜]属性はupdateステートメントに含まれ、id

これはORMの一般的な動作であり、その理由は、あるトランザクションからデータを取得して別のトランザクションから保存している場合(悪い、悪い習慣です!)、データが他のクライアントによって変更された可能性があります。そのため、属性の一部のみを保存すると、矛盾した状態になる可能性があります。

しかし、patch属性の存在そのものがこの概念に違反しています。したがって、本棚は次の方法で改善できます。

  • patchオプションを廃止するだけです。 (私はそれを好むかもしれません)
  • Bookshelfモデルは変更された属性を追跡するため、この点に関して更新をよりスマートにすることは簡単なはずです。この変更により、patchオプションも廃止される可能性があります。
  • 別のアプローチは、patchセマンティクスをsave()で提供されるものだけではなく、変更された属性に関連付けることです。ただし、このような変更により、残念ながら一部のユースケースが機能しなくなる可能性があります。
  • または、最後にnewオプションを導入して、変更されたすべての属性に作用します。しかし、それは面倒に感じます。
6
flaviodesousa

数回の推測の後、私はそれを機能させたように見えますが、これが正しいかどうか、または推測せずにこれをどのように判断できたかはわかりません。

基本的には、「更新」ケースを次のように変更することで機能させることができました。

  • 属性を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);
    });
}

forgeがどのように/ここに収まるのか、または「挿入」の場合のsaveの最初のパラメーターとの取り引きがどうあるべきかはまだわかりません。

さらに重要なことに、私はsetが何のためにあるのかを完全に確信していません。 ORMフレームワークの主な利点の1つは想定のようなものを透明にすることです(つまり、「保存」が正しく機能する一方で、モデルを考えずに、使用せずに使用できます。あなたが「保存」した時点で何が変更されたかについての知識-私すべき事前に未知の任意のコードsetの事柄を知ることができ、何が変更されたかを知らなくてもそれを保存できる、しかしそれは私にはできないように見えます)、それで私が本棚から実際にここで何を得たかはわかりません。より良いアプローチがあるはずです。

2
Jason C