web-dev-qa-db-ja.com

SQLAlchemy-更新の例を選択

SQLAlchemyでselect for updateを使用する完全な例を探していますが、グーグルが1つも見つかりません。単一の行をロックして列を更新する必要があります。次のコードは機能しません(永久にブロックされます)。

s = table.select(table.c.user=="test",for_update=True)
# Do update or not depending on the row
u = table.update().where(table.c.user=="test")         
u.execute(email="foo") 

コミットは必要ですか?それ、どうやったら出来るの?私が知っている限り:トランザクション選択を開始します...更新更新コミット用

29
user317033

ORMを使用している場合は、 with_for_update 関数を試してください。

 foo = session.query(Foo).filter(Foo.id == 1234).with_for_update()。one()
#この行がロックされました
 
 foo.name = 'bar' 
 session.add(foo)
 
 session.commit()
#この行がロック解除されました
22
Matthew Moisen

遅い答えですが、多分誰かがそれを役に立つと思うでしょう

まず、コミットする必要はありません(少なくともクエリの中間ではありません。データベースへの2つの同時接続を効果的に作成しているため、2番目のクエリは無期限にハングします。 1つ目は選択したレコードのロックを取得し、次に2つ目はロックされたレコードを変更しようとします。そのため、正しく機能しません。 (ちなみに、この例では最初のクエリをまったく呼び出さないので、実際のテストではs.execute()のようなものをどこかで実行したと想定しています)。つまり、要は、動作する実装は次のようになるはずです。

s = conn.execute(table.select(table.c.user=="test", for_update=True))
u = conn.execute(table.update().where(table.c.user=="test"), {"email": "foo"})
conn.commit()

もちろん、このような単純なケースでは、ロックを行う理由はありませんが、これは単なる例であり、これら2つの呼び出しの間にロジックを追加することを計画していたと思います。

9
RobertT

はい、コミットする必要があります。これはEngineで実行するか、明示的にTransactionを作成できます。また、修飾子はexecuteではなくvalues(...)メソッドで指定されます。

>>> conn.execute(users.update().
...              where(table.c.user=="test").
...              values(email="foo")
...              ) 
>>> my_engine.commit()
4
van