有没有办法透明地对SQLAlchemy对象执行验证?

Bea*_*sen 17 python dns validation model sqlalchemy

有没有办法在设置属性之后但在提交会话之前对对象执行验证?

例如,我有一个Device具有mac属性的域模型.我想确保在将mac属性添加到数据库或在数据库中更新之前,该属性包含有效且已清理的mac值.

看起来像Pythonic方法是做大多数事情作为属性(包括SQLAlchemy).如果我用PHP或Java编写过这个,我可能会选择创建getter/setter方法来保护数据,并让我灵活地在域模型中处理这个问题.

public function mac() { return $this->mac; }
public function setMac($mac) {
    return $this->mac = $this->sanitizeAndValidateMac($mac);
}
public function sanitizeAndValidateMac($mac) {
    if ( ! preg_match(self::$VALID_MAC_REGEX) ) {
        throw new InvalidMacException($mac);
    }
    return strtolower($mac);
}
Run Code Online (Sandbox Code Playgroud)

什么是使用SQLAlchemy处理此类情况的Pythonic方法?

(虽然我知道验证并且应该在其他地方处理(即Web框架),但我想弄清楚如何处理这些特定于领域的验证规则,因为它们必然经常出现.)

UPDATE

我知道在正常情况下我可以使用财产来做到这一点.关键部分是我在这些类中使用SQLAlchemy.我不明白SQLAlchemy究竟是如何表现它的魔力但我怀疑自己创建和覆盖这些属性可能导致不稳定和/或不可预测的结果.

cul*_*lix 14

您可以使用@validates()装饰器在SQLAlchemy类中添加数据验证.

从文档 - 简单验证器:

属性验证器可以引发异常,停止变更属性值的过程,或者可以将给定值更改为不同的值.

from sqlalchemy.orm import validates

class EmailAddress(Base):
    __tablename__ = 'address'

    id = Column(Integer, primary_key=True)
    email = Column(String)

    @validates('email')
    def validate_email(self, key, address):
        # you can use assertions, such as
        # assert '@' in address
        # or raise an exception:
        if '@' not in address:
            raise ValueError('Email address must contain an @ sign.')
        return address
Run Code Online (Sandbox Code Playgroud)


cvo*_*ogt 8

是.这可以使用MapperExtension很好地完成.

# uses sqlalchemy hooks to data model class specific validators before update and insert
class ValidationExtension( sqlalchemy.orm.interfaces.MapperExtension ):
    def before_update(self, mapper, connection, instance):
        """not every instance here is actually updated to the db, see http://www.sqlalchemy.org/docs/reference/orm/interfaces.html?highlight=mapperextension#sqlalchemy.orm.interfaces.MapperExtension.before_update"""
        instance.validate()
        return sqlalchemy.orm.interfaces.MapperExtension.before_update(self, mapper, connection, instance)
    def before_insert(self, mapper, connection, instance):
        instance.validate()
        return sqlalchemy.orm.interfaces.MapperExtension.before_insert(self, mapper, connection, instance)


sqlalchemy.orm.mapper( model, table, extension = ValidationExtension(), **mapper_args )
Run Code Online (Sandbox Code Playgroud)

您可能需要检查before_update引用,因为此处的每个实例都不会实际更新到db.


S.L*_*ott 2

“看起来 Pythonic 方法是将大多数事情作为属性来做”

它有所不同,但很接近。

“如果我用 PHP 或 Java 进行编码,我可能会选择创建 getter/setter 方法......”

好的。这已经足够 Pythonic 了。您的 getter 和 setter 函数绑定在一个属性中;那很好。

问题是什么?

你问财产怎么拼写?

然而,“透明验证”——如果我正确地阅读了你的示例代码——可能并不是一个好主意。

您的模型和验证可能应该分开。对单个模型进行多次验证是很常见的。对于某些用户来说,字段是可选的、固定的或不使用的;这会导致多次验证。

您会更高兴遵循使用表单进行验证的 Django 设计模式,与模型分开。

  • 我不确定 SQLAlchemy 使用什么样的魔法来绑定到 Model 类属性。我不认为在我的类上定义我自己的“mac = property()”是安全的。我想我的问题是这样做安全吗?如果是这样,有什么陷阱吗?如果没有,还有哪些其他替代方案? (2认同)