我使用unique_constructor与数据库进行交互: https://github.com/sqlalchemy/sqlalchemy/wiki/UniqueObject 但是似乎我使用的是错误的,或者我使用的是postgresql错误的。
我有一个带有2个外键的对象和一个出现计数器。外键是:一个通向具有实际数据的表,另一个通向某些标识表。当数据库中的对象已经存在时,我想增加计数器,否则创建它并设置当前计数。
这是代码:
from sqlalchemy import Column
from sqlalchemy import Integer
from sqlalchemy import String
from sqlalchemy import ForeignKey
from sqlalchemy import create_engine
from sqlalchemy.orm import relationship
from sqlalchemy.orm import sessionmaker
from sqlalchemy.orm.scoping import scoped_session
from sqlalchemy.ext.declarative import declarative_base
BaseTable = declarative_base()
class Database:
engine = create_engine("postgresql://postgres:postgres@127.0.0.1:5432/test", echo=True, pool_recycle=3600)
session = scoped_session(sessionmaker(bind=engine, expire_on_commit=False))
db = Database()
def unique_constructor(db, hashfunc, queryfunc):
"""
https://github.com/sqlalchemy/sqlalchemy/wiki/UniqueObject
"""
def decorate(cls):
def _unique(session, cls, hashfunc, queryfunc, constructor, arg, kw):
cache = getattr(session, '_unique_cache', None)
if cache is None:
session._unique_cache = cache = {}
key = (cls, hashfunc(*arg, **kw))
if key in cache:
return cache[key]
else:
with session.no_autoflush:
q = session.query(cls)
q = queryfunc(q, *arg, **kw)
obj = q.first()
if not obj:
obj = constructor(*arg, **kw)
session.add(obj)
cache[key] = obj
return obj
def _null_init(self, *arg, **kw):
pass
def __new__(cls, bases, *arg, **kw):
# no-op __new__(), called
# by the loading procedure
if not arg and not kw:
return object.__new__(cls)
session = db.session()
def constructor(*arg, **kw):
obj = object.__new__(cls)
obj._init(*arg, **kw)
return obj
return _unique(
session,
cls,
hashfunc,
queryfunc,
constructor,
arg,
kw
)
# note: cls must be already mapped for this part to work
cls._init = cls.__init__
cls.__init__ = _null_init
cls.__new__ = classmethod(__new__)
return cls
return decorate
@unique_constructor(db, lambda value: value, lambda query, value: query.filter(Ident.value == value))
class Ident(BaseTable):
__tablename__ = "ident"
id = Column(Integer, primary_key=True, nullable=False, autoincrement=True)
value = Column(Integer, unique=True)
@unique_constructor(db, lambda data: data, lambda query, data: query.filter(Foo.data == data))
class Foo(BaseTable):
__tablename__ = "foo"
id = Column(Integer, primary_key=True, nullable=False, autoincrement=True)
data = Column(String, unique=True)
@unique_constructor(db, lambda foo, ident: (foo, ident), lambda query, foo, ident: query.filter(
Foo.data == foo.data, Ident.value == ident.value
))
class Bar(BaseTable):
__tablename__ = "bar"
id = Column(Integer, primary_key=True, nullable=False, autoincrement=True)
foo_id = Column(Integer, ForeignKey("foo.id"), nullable=False)
ident_id = Column(Integer, ForeignKey("ident.id"), nullable=False)
cnt = Column(Integer, default=0)
foo = relationship(Foo)
ident = relationship(Ident)
BaseTable.metadata.create_all(db.engine)
if __name__ == "__main__":
b = Bar(foo=Foo(data="abcdef"), ident=Ident(value=38))
b.cnt = 6
db.session.add(b)
db.session.commit()
现在,当我创建新记录时
b = Bar(foo=Foo(data="abcdefgh"), ident=Ident(value=180))
b.cnt = 6
db.session.add(b)
db.session.commit()
一切都很好。现在,当我想更新第二条记录时,问题就开始了:
b = Bar(foo=Foo(data="abcdefgh"), ident=Ident(value=180))
b.cnt += 6
db.session.add(b)
db.session.commit()
第二条记录保持不变,但第一条记录已更新。当我重新运行此代码时,它开始交替更新第一条和第二条记录。
有人可以解释我,我想念什么吗?
谢谢