向属性添加“验证”例程的一种快速方法是使用validates()
装饰器。属性验证器可以引发异常,停止变更属性值的过程,或者可以将给定值更改为不同的值。与所有属性扩展一样,验证器只能由普通的用户级代码调用;当ORM填充对象时它们不会被发出:
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):
assert '@' in address
return address
版本1.0.0中已更改: - 当刷新主键列的新提取值以及一些python或服务器端默认值时,验证器不再在刷新过程中触发。在1.0之前,验证器也可能在这些情况下被触发。
当项目添加到集合时,验证程序还会收到追加追加事件:
from sqlalchemy.orm import validates
class User(Base):
# ...
addresses = relationship("Address")
@validates('addresses')
def validate_address(self, key, address):
assert '@' in address.email
return address
缺省情况下,验证功能不会针对集合删除事件发出,因为典型的期望是被丢弃的值不需要验证。However, validates()
supports reception of these events by specifying include_removes=True
to the decorator. 当设置此标志时,验证函数必须接收一个额外的布尔参数,如果True
表明该操作是删除:
from sqlalchemy.orm import validates
class User(Base):
# ...
addresses = relationship("Address")
@validates('addresses', include_removes=True)
def validate_address(self, key, address, is_remove):
if is_remove:
raise ValueError(
"not allowed to remove items from the collection")
else:
assert '@' in address.email
return address
使用include_backrefs=False
选项也可以定制相互依赖的验证器通过backref链接的情况;当设置为False
时,此选项可防止发生验证功能,如果事件是由于backref引起的:
from sqlalchemy.orm import validates
class User(Base):
# ...
addresses = relationship("Address", backref='user')
@validates('addresses', include_backrefs=False)
def validate_address(self, key, address):
assert '@' in address.email
return address
Above, if we were to assign to Address.user
as in some_address.user = some_user
, the validate_address()
function would not be emitted, even though an append occurs to some_user.addresses
- the event is caused by a backref.
请注意,validates()
装饰器是建立在属性事件之上的便利函数。需要更多控制属性更改行为配置的应用程序可以使用此系统,如AttributeEvents
所述。
sqlalchemy.orm.
validates
(*names, **kw)¶装饰一个方法作为一个或多个命名属性的“验证器”。
将方法指定为验证程序,该方法接收属性的名称以及要分配的值,或者在集合的情况下,将要添加到集合的值。然后该函数可以引发验证异常,以阻止进程继续进行(Python内置的ValueError
和AssertionError
异常是合理的选择),或者可以修改或替换之前的值诉讼。函数应返回给定的值。
请注意,集合的验证程序不能在验证例程中发出该集合的加载 - 此用法引发一个断言以避免递归溢出。这是一个不支持的重入条件。
参数: |
|
---|
也可以看看
为属性生成修改后行为的更全面的方法是使用descriptors。这些通常在Python中使用property()
函数使用。描述符的标准SQLAlchemy技术是创建一个简单的描述符,并使其从具有不同名称的映射属性读取/写入。下面我们使用Python 2.6样式的属性来说明这一点:
class EmailAddress(Base):
__tablename__ = 'email_address'
id = Column(Integer, primary_key=True)
# name the attribute with an underscore,
# different from the column name
_email = Column("email", String)
# then create an ".email" attribute
# to get/set "._email"
@property
def email(self):
return self._email
@email.setter
def email(self, email):
self._email = email
上述方法可行,但我们可以添加更多内容。While our EmailAddress
object will shuttle the value through the email
descriptor and into the _email
mapped attribute, the class level EmailAddress.email
attribute does not have the usual expression semantics usable with Query
. 为了提供这些,我们改用如下的hybrid
扩展名:
from sqlalchemy.ext.hybrid import hybrid_property
class EmailAddress(Base):
__tablename__ = 'email_address'
id = Column(Integer, primary_key=True)
_email = Column("email", String)
@hybrid_property
def email(self):
return self._email
@email.setter
def email(self, email):
self._email = email
.email
属性除了在我们拥有EmailAddress
实例时提供getter / setter行为外,还在类级别使用时提供了SQL表达式,也就是说,直接从EmailAddress
类:
from sqlalchemy.orm import Session
session = Session()
sqladdress = session.query(EmailAddress).\
filter(EmailAddress.email == 'address@example.com').\
one()
SELECT address.email AS address_email, address.id AS address_id
FROM address
WHERE address.email = ?
('address@example.com',)
address.email = 'otheraddress@example.com'
sqlsession.commit()
UPDATE address SET email=? WHERE address.id = ?
('otheraddress@example.com', 1)
COMMIT
hybrid_property
还允许我们改变属性的行为,包括定义在实例级别与类/表达级别访问属性时使用hybrid_property.expression()
修饰符。比如,如果我们想自动添加一个主机名,我们可以定义两组字符串操作逻辑:
class EmailAddress(Base):
__tablename__ = 'email_address'
id = Column(Integer, primary_key=True)
_email = Column("email", String)
@hybrid_property
def email(self):
"""Return the value of _email up until the last twelve
characters."""
return self._email[:-12]
@email.setter
def email(self, email):
"""Set the value of _email, tacking on the twelve character
value @example.com."""
self._email = email + "@example.com"
@email.expression
def email(cls):
"""Produce a SQL expression that represents the value
of the _email column, minus the last twelve characters."""
return func.substr(cls._email, 0, func.length(cls._email) - 12)
以上,访问EmailAddress
实例的email
属性将返回_email
属性的值,删除或添加主机名@example.com
中的值。当我们查询email
属性时,会呈现一个SQL函数,它会产生相同的效果:
sqladdress = session.query(EmailAddress).filter(EmailAddress.email == 'address').one()
SELECT address.email AS address_email, address.id AS address_id
FROM address
WHERE substr(address.email, ?, length(address.email) - ?) = ?
(0, 12, 'address')
在Hybrid Attributes中了解更多关于混合动力的信息。
同义词是映射级别的结构,它允许类的任何属性“镜像”映射的另一个属性。
从最基本的意义上说,同义词是通过附加名称提供某个特定属性的简单方法:
class MyClass(Base):
__tablename__ = 'my_table'
id = Column(Integer, primary_key=True)
job_status = Column(String(50))
status = synonym("job_status")
上面的类MyClass
具有两个属性,即.job_status
和.status
,它们在表达式级别上表现为一个属性:
>>> print(MyClass.job_status == 'some_status')
my_table.job_status = :job_status_1
>>> print(MyClass.status == 'some_status')
my_table.job_status = :job_status_1
并在实例级别:
>>> m1 = MyClass(status='x')
>>> m1.status, m1.job_status
('x', 'x')
>>> m1.job_status = 'y'
>>> m1.status, m1.job_status
('y', 'y')
synonym()
可以用于任何类型的映射属性,该属性的子类为MapperProperty
,包括映射列和关系以及同义词本身。
除了简单的镜像之外,也可以使用synonym()
来引用用户定义的descriptor。我们可以用@property
提供我们的status
同义词:
class MyClass(Base):
__tablename__ = 'my_table'
id = Column(Integer, primary_key=True)
status = Column(String(50))
@property
def job_status(self):
return "Status: " + self.status
job_status = synonym("status", descriptor=job_status)
当使用Declarative时,可以使用synonym_for()
装饰器更简洁地表达上述模式:
from sqlalchemy.ext.declarative import synonym_for
class MyClass(Base):
__tablename__ = 'my_table'
id = Column(Integer, primary_key=True)
status = Column(String(50))
@synonym_for("status")
@property
def job_status(self):
return "Status: " + self.status
虽然synonym()
对于简单镜像很有用,但使用hybrid attribute特性更好地处理了在描述符中增强属性行为的用例,朝向Python描述符。从技术上讲,一个synonym()
可以完成hybrid_property
所能做的所有事情,因为它还支持注入自定义SQL功能,但混合使用更为简单的情况。
sqlalchemy.orm.
synonym
(name, map_column=None, descriptor=None, comparator_factory=None, doc=None, info=None)¶将属性名称表示为映射属性的同义词,因为该属性将镜像另一个属性的值和表达式行为。
参数: |
|
---|
SQLAlchemy ORM和Core表达式语言使用的“操作符”是完全可定制的。例如,比较表达式User.name == 'ed'
使用Python内置的运算符本身称为operator.eq
- 可修改SQLAlchemy与此类运算符关联的实际SQL构造。新操作也可以与列表达式相关联。发生在列表达式上的运算符在类型级别上被直接重新定义 - 请参阅Redefining and Creating New Operators部分的描述。
ORM level functions like column_property()
, relationship()
, and composite()
also provide for operator redefinition at the ORM level, by passing a PropComparator
subclass to the comparator_factory
argument of each function. 在这个级别定制运营商是一种罕见的用例。请参阅PropComparator
中的文档以获取概述。