Le problème est que vous voulez vous assurer que les instances que vous créez sont uniques. Nous pouvons créer un constructeur alternatif qui vérifie un cache d'instances non validées existantes ou interroge la base de données pour une instance validée existante avant de renvoyer une nouvelle instance.
Voici une démonstration d'une telle méthode :
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
Le create_unique
méthode a été inspirée par l'exemple du wiki SQLAlchemy
. Cette version est beaucoup moins alambiquée, privilégiant la simplicité à la flexibilité. Je l'ai utilisé dans des systèmes de production sans problème.
Il y a évidemment des améliorations qui peuvent être ajoutées; ce n'est qu'un exemple simple. Le get_unique
la méthode peut être héritée d'un UniqueMixin
, à utiliser pour n'importe quel nombre de modèles. Une mémorisation plus souple des arguments pourrait être mise en œuvre. Cela met également de côté le problème des threads multiples insérant des données contradictoires mentionné par Ants Aasma; une manipulation plus complexe mais qui devrait être une extension évidente. Je vous laisse cela.