1. ホーム
  2. python

[解決済み] SQLAlchemy には、Django の get_or_create に相当するものがありますか?

2022-04-14 03:11:15

質問

オブジェクトがすでに存在する場合はデータベースから取得し、存在しない場合は作成したいのですが、どうすればいいですか?

Django の get_or_create (または ソース ) がこれを行います。 SQLAlchemy に同等のショートカットはありますか?

現在、このように明示的に書き出しています。

def get_or_create_instrument(session, serial_number):
    instrument = session.query(Instrument).filter_by(serial_number=serial_number).first()
    if instrument:
        return instrument
    else:
        instrument = Instrument(serial_number)
        session.add(instrument)
        return instrument

解決方法は?

基本的にはその方法で、すぐに使える近道はありません。

もちろん、それを一般化することもできます。

def get_or_create(session, model, defaults=None, **kwargs):
    instance = session.query(model).filter_by(**kwargs).one_or_none()
    if instance:
        return instance, False
    else:
        params = {k: v for k, v in kwargs.items() if not isinstance(v, ClauseElement)}
        params.update(defaults or {})
        instance = model(**params)
        try:
            session.add(instance)
            session.commit()
        except Exception:  # The actual exception depends on the specific database so we catch all exceptions. This is similar to the official documentation: https://docs.sqlalchemy.org/en/latest/orm/session_transaction.html
            session.rollback()
            instance = session.query(model).filter_by(**kwargs).one()
            return instance, False
        else:
            return instance, True


2020年アップデート (Python 3.9+ ONLY)

Python 3.9のものを使ったよりきれいなバージョンはこちらです。 新しいディクショナリーユニオン演算子(|=)

def get_or_create(session, model, defaults=None, **kwargs):
    instance = session.query(model).filter_by(**kwargs).one_or_none()
    if instance:
        return instance, False
    else:
        kwargs |= defaults or {}
        instance = model(**kwargs)
        try:
            session.add(instance)
            session.commit()
        except Exception:  # The actual exception depends on the specific database so we catch all exceptions. This is similar to the official documentation: https://docs.sqlalchemy.org/en/latest/orm/session_transaction.html
            session.rollback()
            instance = session.query(model).filter_by(**kwargs).one()
            return instance, False
        else:
            return instance, True

注意事項

Django 版と同様に、これは重複キー制約と同様のエラーを捕捉します。get や create が単一の結果を返すことが保証されていない場合、レースコンディションを 引き起こす可能性があります。

この問題を軽減するために、もう1つ別の one_or_none() の直後にスタイルフェッチを行います。 session.commit() . を使用しない限り、競合状態に対する100%の保証はありません。 with_for_update() またはシリアライザブル・トランザクション・モードを使用します。