使用SQLAlchemy自动哈希主键,并在flush/commit时使其持久化。

5

我正在尝试在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()之后分配的。 但是,在flush期间,对象状态的更改不会持久化。

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

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启动了一个UPDATE,并将user.hash持久化。

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

还有其他选项吗? 我真的很希望可以在after_insert之后自动进行哈希。 也许我在ORM事件文档中错过了什么?

1个回答

8

这是一个很好的after_insert()方法使用场景,因为这是新生成的PK首次被一致地提供(也不需要SELECT,但不确定您所说的“延迟加载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
    )

有没有一种优雅的方式将这些更改传播回新创建的对象?@zzzeek - user762391
1
如果您使用set_committed_value(object, "hash", myhash)设置值,则可以避免触发历史记录:http://docs.sqlalchemy.org/en/rel_0_7/orm/session.html?highlight=set_committed_value#sqlalchemy.orm.attributes.set_committed_value - zzzeek
有没有其他方法可以直接使用http://docs.sqlalchemy.org/en/latest/core/defaults.html#column-insert-update-defaults中的默认上下文敏感函数来生成hashid?或者只能使用`event.listens_for`来完成这项工作。我几乎找不到任何解决方案。 - Ryan Chou
有没有一种在插入之前进行哈希的方法?对我来说,插入然后更新不是一个很好的方法,因为这是两个具有相同目标的操作。 - Pstr

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接