4

我正在尝试在 sqlalchemy 中获取对象主键的哈希值。

一个示例class User如下所示:

class User(base):
    __tablename__ == 'users'

    id = Column(Integer, primary_key=True)
    hash = Column(String(38), unique=True)

并添加用户:

user = User()
session.add(user)
session.commit()

print user.id

我试图将散列过程挂钩到 ORM 事件中,因为主键是在session.flush(). 但是在刷新期间,对对象状态的更改不会持久化。

唯一对我有用的解决方案是在第一次刷新之后延迟加载,user.id然后再进行哈希、分配和刷新,最后提交。

class User(base):
...

    @hybrid_property
    def hash(self):
        if not self.hash:
            self.hash = hash_function(self.id)
        return self.hash

这使我能够执行以下操作:

user = User()
session.add(user)
hash = user.hash
session.commit()

sqlalchemy 启动 anUPDATE和 theuser.hash并使其持久化。

但这似乎是一个糟糕的选择,例如,如果有人忘记分配散列(也许在创建对象时不需要散列)。

还有其他选择吗?我真的更喜欢它自动散列after_insert。也许我在 ORM 事件文档中遗漏了一些东西?

4

1 回答 1

8

这是 after_insert() 的好地方,因为这是第一次新生成的 PK 始终可用(它也不需要选择,但不确定“延迟加载 user.id”是什么意思,它应该被分配),你要做的是直接发出一个 UPDATE 语句。这就是映射器级别刷新事件被赋予连接(而不是会话)的原因。

应该像这样简单:

@event.listens_for(User, "after_insert")
def update_hash(mapper, connection, target):
    user_table = mapper.local_table
    connection.execute(
          user_table.update().
              values(hash=hash_function(target.id)).
              where(user_table.c.id==target.id)
    )

如果你想让这对许多用户更有效,你可以在 after_flush() 事件中做到这一点:

@event.listens_for(Session, "after_flush")
def update_hash(session, flush_context):
    hashes = [
        {"user_id":element.id, "hash":hash_function(element.id)}
        for element in session.new if isinstance(element, User)
    ]
    user_table = mapper.local_table
    session.execute(
          user_table.update().
              values(hash=bind('hash')).
              where(user_table.c.id==bind('user_id')),
          hashes
    )
于 2012-09-09T17:43:07.200 回答