El problema es que desea asegurarse de que las instancias que cree sean únicas. Podemos crear un constructor alternativo que verifique un caché de instancias no confirmadas existentes o consulte la base de datos para una instancia confirmada existente antes de devolver una nueva instancia.
Aquí hay una demostración de dicho método:
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
El create_unique
El método se inspiró en el ejemplo del wiki de SQLAlchemy
. Esta versión es mucho menos complicada y favorece la simplicidad sobre la flexibilidad. Lo he usado en sistemas de producción sin problemas.
Obviamente, hay mejoras que se pueden agregar; esto es solo un ejemplo simple. El get_unique
el método podría ser heredado de un UniqueMixin
, para ser utilizado para cualquier número de modelos. Se podría implementar una memorización de argumentos más flexible. Esto también deja de lado el problema de múltiples subprocesos que insertan datos en conflicto mencionados por Ants Aasma; manejo que es más complejo pero debería ser una extensión obvia. Eso te lo dejo a ti.