Проблемът е, че искате да сте сигурни, че екземплярите, които създавате, са уникални. Можем да създадем алтернативен конструктор, който проверява кеш на съществуващи некомитирани екземпляри или да прави запитвания към базата данни за съществуващ комитиран екземпляр, преди да върне нов екземпляр.
Ето демонстрация на такъв метод:
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
методът е вдъхновен от пример от уикито на SQLAlchemy
. Тази версия е много по-малко сложна, предпочитайки простотата пред гъвкавостта. Използвал съм го в производствени системи без проблеми.
Очевидно има подобрения, които могат да бъдат добавени; това е просто прост пример. get_unique
метод може да бъде наследен от UniqueMixin
, да се използва за произволен брой модели. Може да се приложи по-гъвкаво запаметяване на аргументи. Това също оставя настрана проблема с множество нишки, вмъкващи противоречиви данни, споменати от Ants Aasma; обработка, която е по-сложна, но трябва да бъде очевидно разширение. Оставям това на вас.