如何在运行验证程序时使用字典数据更新声明性SQLAlchemy模型

时间:2017-06-13 07:41:50

标签: python sqlalchemy

问题实际上是如何更新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'

2 个答案:

答案 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.getQuery.filter.all上的循环

  • 检查约束

  • 触发器和存储过程