这个问题困扰了我很久,为什么一直没解决,主要是因为有替换方案可以选择,所以一直搁置了。
现在随着业务发展,数据结构、存储算法越来越复杂,绕路的方案在某些时候就变得不可接受了,所以需要去解决这个问题。
正常的 ORM 模式,如果需要更新某条数据的话,有两个办法。
user = session.query(User).filter(User.id == 1).first()
user.name = 'KL'
session.commit()
或者
session.query(User).filter(User.id == 1).update({
'name': 'KL'
})
我目前碰到的问题是,第一种方式不可用,第二种方式可用。
熟悉 ORM 的朋友应该都知道,第二种方式是即时生效的,默认带上了 commit 方法。
这就导致每次更新都会去请求数据库进行插入操作,非常耗时。在业务简单的时候,影响不大,但是随着业务发展,这种更新方式在有些时候就变得不可接受了。
最初我非常懵逼,不明白为什么会不可用,然后我重新看了官方文档里数据更新相关的章节。
找到了两个可以查看 session 缓存的方法。
session.dirty
session.new
session.dirty
表示和当前数据表不一致的数据,也就是你修改过的数据。
session.new
表示不再当前数据表的数据,也就是你新增的数据。
当我使用第一种方法去更新数据时,发现 session.dirty
中没有存入任何数据,这让我很惊讶,经验告诉我,这是在赋值的时候就出现了问题。
于是我找到了我的映射类。
class Account(zda_engine.Base):
__tablename__ = 'account'
id = Column(Integer, primary_key=True, autoincrement=True, nullable=False, unique=True)
email = Column(String, nullable=False)
password = Column(String, nullable=False)
def __setattr__(self, key, value):
if key == 'password':
self.__dict__[key] = hashlib.md5(value.encode(encoding='UTF-8')).hexdigest()
else:
self.__dict__[key] = value
为了方便展示,我把__setattr__
方法移到了子类中,实际使用时,更复杂一些。
可以看到,我在自己定义的类里面完全重写了__setattr__
方法,一直以来这个方法可以正常的更新实例值,所以我一直觉得没有问题。
不过在session.dirty
方法中,我判断了一下是设置值时出现的问题,那么再看这段代码时,就感觉处处是问题了。
最关键的就是,它覆盖了 SqlAlchemy 的__setattr__
方法,最终导致后续一系列的数据存储动作都出现了问题,因为代码没跑过去。
当然,在正式修改成功之前,这都是我的推测,于是我将__setattr__
代码修改为下面这样
def __setattr__(self, key, value):
if key == 'password':
value = hashlib.md5(value.encode(encoding='UTF-8')).hexdigest()
super().__setattr__(key, value)
这样的话,既能将特定的字段修改为我想要的值,同时依旧可以调用基类中的__setattr__
方法。
经过调试,最终解决这个问题。
基础方法使用时,会带来非常多非常好的体验,同时也可能会和其他广泛使用的三方库发生冲突,在使用这些方法时,最好要重新调用一下基类方法,然后再去实现自己的逻辑。