問題は、作成するインスタンスが一意であることを確認する必要があることです。新しいインスタンスを返す前に、既存のコミットされていないインスタンスのキャッシュをチェックしたり、データベースに既存のコミットされたインスタンスを照会したりする代替コンストラクターを作成できます。
このような方法のデモンストレーションは次のとおりです。
from sqlalchemy import Column, Integer, String, ForeignKey, Table
from sqlalchemy.engine import create_engine
from sqlalchemy.ext.declarative.api import declarative_base
from sqlalchemy.orm import sessionmaker, relationship
engine = create_engine('sqlite:///:memory:', echo=True)
Session = sessionmaker(engine)
Base = declarative_base(engine)
session = Session()
class Role(Base):
__tablename__ = 'role'
id = Column(Integer, primary_key=True)
name = Column(String, nullable=False, unique=True)
@classmethod
def get_unique(cls, name):
# get the session cache, creating it if necessary
cache = session._unique_cache = getattr(session, '_unique_cache', {})
# create a key for memoizing
key = (cls, name)
# check the cache first
o = cache.get(key)
if o is None:
# check the database if it's not in the cache
o = session.query(cls).filter_by(name=name).first()
if o is None:
# create a new one if it's not in the database
o = cls(name=name)
session.add(o)
# update the cache
cache[key] = o
return o
Base.metadata.create_all()
# demonstrate cache check
r1 = Role.get_unique('admin') # this is new
r2 = Role.get_unique('admin') # from cache
session.commit() # doesn't fail
# demonstrate database check
r1 = Role.get_unique('mod') # this is new
session.commit()
session._unique_cache.clear() # empty cache
r2 = Role.get_unique('mod') # from database
session.commit() # nop
# show final state
print session.query(Role).all() # two unique instances from four create calls
create_unique
メソッドは、SQLAlchemywikiの例
に触発されました。 。このバージョンはそれほど複雑ではなく、柔軟性よりも単純さを優先しています。実稼働システムで問題なく使用しました。
追加できる改善点が明らかにあります。これは単純な例です。 get_unique
メソッドはUniqueMixin
から継承できます 、任意の数のモデルに使用されます。引数のより柔軟なメモ化を実装できます。これにより、複数のスレッドがAntsAasmaによって言及された競合するデータを挿入するという問題も回避されます。より複雑ですが、明らかな拡張であるはずの処理。任せます。