Problemet är att du vill se till att instanserna du skapar är unika. Vi kan skapa en alternativ konstruktor som kontrollerar en cache för befintliga icke-kommitterade instanser eller frågar databasen efter befintliga commited-instanser innan vi returnerar en ny instans.
Här är en demonstration av en sådan metod:
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
Metoden var inspirerad av exemplet från SQLAlchemy-wikin
. Denna version är mycket mindre invecklad och gynnar enkelhet framför flexibilitet. Jag har använt det i produktionssystem utan problem.
Det finns uppenbarligen förbättringar som kan läggas till; detta är bara ett enkelt exempel. get_unique
metod kan ärvas från en UniqueMixin
, att användas för valfritt antal modeller. Mer flexibel memoisering av argument skulle kunna implementeras. Detta lägger också åt sidan problemet med flera trådar som infogar motstridiga data som nämns av Ants Aasma; hantering som är mer komplex men borde vara en självklar förlängning. Jag lämnar det till dig.