python - update - SqlAlchemyでupsertを実行するには?




upsert postgresql (2)

SQLAlchemyには、最近のバージョンではsession.addに組み込まれていましたが、これまでは別のsession.saveorupdate呼び出しであった「保存または更新」の動作があります。 これは「アップサート」ではありませんが、あなたのニーズに十分対応できるかもしれません。

複数の一意のキーを持つクラスについて質問するのは良いことです。 私はこれが正確にこれを行う正しい方法がないという理由であると確信しています。 プライマリキーも一意のキーです。 ユニークな制約がなく、プライマリキーのみが存在する場合は、十分な単純な問題になります。指定されたIDを持つものがない場合、またはIDがNoneの場合は、新しいレコードを作成します。 そうでない場合は、既存レコードの他のすべてのフィールドをその主キーで更新します。

しかし、さらに固有の制約がある場合、その単純なアプローチには論理的な問題があります。 オブジェクトを「アップサンプリング」し、オブジェクトの主キーが既存のレコードと一致し、別の一意の列が別のレコードと一致する場合 、あなたは何をしますか? 同様に、主キーが既存のレコードと一致しないが、別の一意の列既存のレコードと一致する場合は、何ですか? あなたの特定の状況に正解があるかもしれませんが、一般的に私は正解が1つしかないと主張します。

これは、「upsert」オペレーションが組み込まれていない理由です。 アプリケーションは、それぞれの特定のケースでこれが何を意味するのかを定義しなければなりません。

データベースに存在しない場合はレコードが存在し、すでに存在する場合(プライマリキーが存在する場合)、フィールドを現在の状態に更新する必要があります。 これはしばしばアップサートと呼ばれます。

以下の不完全なコードスニペットは何がうまくいくかを示しますが、(特に列がもっと多い場合は)過度にぎこちないようです。 より良い/最良の方法は何ですか?

Base = declarative_base()
class Template(Base):
    __tablename__ = 'templates'
    id = Column(Integer, primary_key = True)
    name = Column(String(80), unique = True, index = True)
    template = Column(String(80), unique = True)
    description = Column(String(200))
    def __init__(self, Name, Template, Desc):
        self.name = Name
        self.template = Template
        self.description = Desc

def UpsertDefaultTemplate():
    sess = Session()
    desired_default = Template("default", "AABBCC", "This is the default template")
    try:
        q = sess.query(Template).filter_by(name = desiredDefault.name)
        existing_default = q.one()
    except sqlalchemy.orm.exc.NoResultFound:
        #default does not exist yet, so add it...
        sess.add(desired_default)
    else:
        #default already exists.  Make sure the values are what we want...
        assert isinstance(existing_default, Template)
        existing_default.name = desired_default.name
        existing_default.template = desired_default.template
        existing_default.description = desired_default.description
    sess.flush()

これを行うためのより良い方法またはそれほど冗長な方法はありませんか? このようなものは素晴らしいでしょう:

sess.upsert_this(desired_default, unique_key = "name")

unique_key kwargは明らかに不要です(ORMはこれを簡単に把握できるはずunique_key 。SQLAlchemyがプライマリキーのみで動作する傾向があるからです。 例:私はSession.mergeが適用可能かどうかを見てきましたが、これはプライマリキーでのみ機能します。この場合、この目的ではあまり有用ではない自動インクリメントIDです。

これのサンプルユースケースは、デフォルトの予期されたデータをアップグレードした可能性のあるサーバーアプリケーションを起動するときだけです。 つまり、このアップサートに対する並行性の懸念はありません。


SQLAlchemyは、 on_conflict_do_update()on_conflict_do_nothing() 2つのメソッドを持つON CONFLICTサポートしています。

ドキュメントからのコピー:

from sqlalchemy.dialects.postgresql import insert

stmt = insert(my_table).values(user_email='[email protected]', data='inserted data')
stmt = stmt.on_conflict_do_update(
    index_elements=[my_table.c.user_email],
    index_where=my_table.c.user_email.like('%@gmail.com'),
    set_=dict(data=stmt.excluded.data)
    )
conn.execute(stmt)

http://docs.sqlalchemy.org/en/latest/dialects/postgresql.html?highlight=conflict#insert-on-conflict-upsert







upsert