3

私はTrackテーブルとテーブルを持っていArtistます。Artistテーブルには がidあり、テーブルTrackには外部キー がありartist_idます。関連するトラックがないすべてのアーティストを削除したい。

SQL では:

delete from artists 
 where id in (select artists.id
                from artists left outer join tracks 
                  on tracks.artist_id = artists.id
               where tracks.id is null);

これは完全に機能します。しかし、SQLAlchemy でこれを複製しようとすると:

artists = session.query(Artist.id).outerjoin((Track, Artist.id == Track.artist_id)).filter(Track.id == None)
print('deleting %d unused artists' % artists.count())
session.query(Artist).filter(Artist.id.in_(artists.all())).delete()

正常にprint()動作します(正しい行数が表示されます)が、削除するとエラーが発生します:

...
sqlalchemy.orm.evaluator.UnevaluatableError: Cannot evaluate clauselist with operator <function comma_op at 0x2d3e0d8>
During handling of the above exception, another exception occurred:
...
"Could not evaluate current criteria in Python. "
sqlalchemy.exc.InvalidRequestError: Could not evaluate current criteria in Python. Specify 'fetch' or False for the synchronize_session parameter.

では、SQLAlchemy でこれを行うにはどうすればよいでしょうか。トラックに属さないアーティストをすべて削除できるのであれば、アプローチが異なっていてもかまいません。

PS私もartists.delete()(上記のIDの代わりにArtistインスタンスを選択しながら)試してみましたが、これもエラーが発生しました-その場合、外部結合が「失われ」、SQLに一貫性がありません。

アップデート

artistsこれが誰にとっても役立つ場合、モデルに backref (下の )がある場合は、外部結合よりもはるかに簡単なアプローチがあります。

session.query(Artist).filter(Artist.tracks == None).delete(synchronize_session=False)
4

1 に答える 1

4

例外は、問題を修正する方法を正確に示しているため、をまたは のいずれかsynchronize_sessionとして指定する必要があります。Sqlalchemy は、Python オブジェクトに直接削除を適用することでデータベースの変更を反映する ために、に接続されているオブジェクトの状態を更新することで、余分な作業を回避しようとしています。"fetch"Falsesession

残念ながら、このクエリではそれを行うことができないため、アクティブな を「フェッチ」してArtist削除されたかどうかを確認するか、単に無視してセッションを無効な状態のままにするように指示する必要があります。

また、in_(query.all())プロダクションで問題が発生することもあります。sqlalchemy はタプルのリストをバインドする方法を知らず、元のクエリの不完全な再現でもあるためです。だけin_(query)で十分です。

于 2013-02-18T05:40:37.777 に答える