4

ORMは「更新して返す」ことができないため、SQLalchemyコア式を使用してオブジェクトをフェッチする必要があります。(ORM の更新にはありませんreturning)

from sqlalchemy import update
class User(ORMBase): 
    ...
# pure sql expression, the object returned is not ORM object.
# the object is a RowProxy.
object = update(User)  \
    .values({'name': 'Wayne'})  \
    .where(User.id == subquery.as_scalar()) \
    .returning() \
    .fetchone()

いつ

db_session.add(object)

報告しUnmappedInstanceError: Class 'sqlalchemy.engine.result.RowProxy' is not mappedます。

RowProxyそのオブジェクトをSQL式からORMのIDマップに入れるにはどうすればよいですか?

4

2 に答える 2

1

単純なケース:

考えられる簡単な解決策: からオブジェクトを構築しますkwargsRowProxyこれらはオブジェクトに似ているためです。

与えられた:

rowproxy = update(User)  \
    .values({'name': 'Wayne'})  \
    .where(User.id == subquery.as_scalar()) \
    .returning() \
    .fetchone()

次のことができるかもしれません:

user = User(**dict(rowproxy.items()))

rowproxy.items()ペアtuplesのリターン; を実際のペアに変換します。と属性名を取ります。key-valuedict(...)tupleskey-valueUser(...)kwargsmodel

より困難なケース:

しかしmodel、 の 1 つがattribute namesSQL とまったく同じでない場合はどうなるtable column nameでしょうか。例:

class User(ORMBase):
    # etc...
    user_id = Column(name='id', etc)

クラスにアンパックしようとするとrowproxyUser次の行に沿ってエラーが発生する可能性があります(代わりにTypeError: 'id' is an invalid keyword argument for User期待しているため)。user_id

今では汚れています:属性から属性へ、またはその逆mapperに取得する方法については、周りにあるはずです:tablemodel

kw_map = {a.key: a.class_attribute.name for a in User.__mapper__.attrs}

ここで、a.keymodel attribute(およびkwarg) であり、a.class_attribute.nametable attributeです。これにより、次のような結果が得られます。

{
    "user_id": "id"
}

さて、実際に から返された値を提供したいと思いますrowproxy。これは、オブジェクトのようなアクセスを許可するだけでなく、dict のようなアクセスも許可します。

kwargs = {a.key: rowproxy[a.class_attribute.name] for a in User.__mapper__.attrs}

そして今、私たちはできる:

user = User(**kwargs)

正誤表:

  • 変更がデータベースに永続的に保存されるときと比較して、変更からの長い遅延を防ぐためsession.commit()に、呼び出しの直後にしたい場合があります。update().returning()後でする必要はありませんsession.add(user)- あなたはすでにそのトランザクションupdated()を必要としていますcommit()
  • objectは Python のキーワードなので、踏まないようにしてください。これを行うと、非常に奇妙な動作が発生する可能性があります。そのため、名前を に変更しましたrowproxy
于 2017-11-09T23:55:59.003 に答える