问题实际上是如何更新SQLAlchemy声明性模型以便它运行验证器。在我的情况下,使用像User.name = name
这样的setter并不是一个真正的选择。
以下是我的意思的可运行示例
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy import Column, String, Integer
from sqlalchemy.orm import validates
from sqlalchemy.ext.declarative import declarative_base
some_engine = create_engine('sqlite://')
Session = sessionmaker(bind=some_engine)
session = Session()
Base = declarative_base()
class User(Base):
__tablename__ = 'user'
id = Column(Integer, primary_key=True)
name = Column(String, nullable=False)
@validates('name')
def validate_name(self, key, value):
if value != 'asd':
raise ValueError('not asd')
return value
Base.metadata.create_all(bind=some_engine)
user = User(id=1, name='qwe')
# >>> ValueError: not asd
user = User(id=1, name='asd')
session.add(user)
session.commit()
session.query(User).filter(User.id=1).update({'name': 'qwe'})
session.query(User).filter(User.id==1)[0].name
# >>> 'qwe'
答案 0 :(得分:1)
你可以在你的模型中添加一个mixin,它提供了一个非常简单的更新方法,它只使用setattr()
来设置实例的属性。
class UpdateMixin:
"""
Add a simple update() method to instances that accepts
a dictionary of updates.
"""
def update(self, values):
for k, v in values.items():
setattr(self, k, v)
然后将用户类定义为
class User(UpdateMixin, Base):
...
要从给定字典更新单个实例,您可以运行
session.query(User).get(1).update({ 'name': 'qwe' })
# or since you have the user instance from before
user.update({ 'name': 'qwe' })
请注意Query.get()
的使用。如果没有具有给定id的用户,它将返回None并尝试调用方法更新将引发。另一个需要注意的是,如果在引发任何异常时不进行回滚,则无法预测发生了什么(如果有)更新(已添加到会话中),因为字典没有排序。所以总是回滚任何错误。
我还建议实际命名方法updateSelf
或其他方法,以防止将其与Query.update()
混淆的风险。
答案 1 :(得分:1)
简短的回答是,当您需要模型级别约束时,不要使用query.update。它恰好适用于性能比执行这些类型的模型级别约束更重要的时间。其他答案提供了解决方案的细节,但基本答案是Query.update并不是为了强制执行python级别的约束。 一般类别的解决方案是:
使用一些会话级方法和Query.get
或Query.filter.all
上的循环
检查约束
触发器和存储过程