Il problema è che vuoi assicurarti che le istanze che crei siano uniche. Possiamo creare un costruttore alternativo che controlli una cache delle istanze esistenti senza commit o interroga il database per l'istanza esistente con commit prima di restituire una nuova istanza.
Ecco una dimostrazione di tale metodo:
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
Il create_unique
è stato ispirato dall'esempio dal wiki SQLAlchemy
. Questa versione è molto meno contorta, favorendo la semplicità rispetto alla flessibilità. L'ho usato nei sistemi di produzione senza problemi.
Ci sono ovviamente miglioramenti che possono essere aggiunti; questo è solo un semplice esempio. Il get_unique
il metodo potrebbe essere ereditato da un UniqueMixin
, da utilizzare per un numero qualsiasi di modelli. Potrebbe essere implementata una memorizzazione più flessibile degli argomenti. Questo mette da parte anche il problema dei thread multipli che inseriscono dati contrastanti menzionati da Ants Aasma; gestione che è più complessa ma dovrebbe essere un'estensione ovvia. Lo lascio a te.