更改属性行为
简单验证器
向属性添加“验证”例程的快速方法是使用 validates()
装饰者。属性验证器可以引发异常,停止改变属性值的过程,或者将给定值更改为其他值。与所有属性扩展一样,验证程序只由普通的userland代码调用;当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): if '@' not in address: raise ValueError("failed simple email validation") 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): if '@' not in address.email: raise ValueError("failed simplified email validation") return address
默认情况下,不会为集合移除事件发出验证函数,因为通常期望丢弃的值不需要验证。然而, validates()
通过指定 include_removes=True
给装饰师。设置此标志时,验证函数必须接收一个附加的布尔参数,如果 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: if '@' not in address.email: raise ValueError("failed simplified email validation") return address
如果相互依赖的验证器通过backref链接,也可以使用 include_backrefs=False
选项;此选项,当设置为 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): if '@' not in address: raise ValueError("failed simplified email validation") return address
上面,如果我们要分配给 Address.user
如在 some_address.user = some_user
, the validate_address()
函数将 not 即使发生附加 some_user.addresses
-事件是由backref引起的。
请注意 validates()
decorator是一个建立在属性事件之上的方便函数。需要更多控制属性更改行为配置的应用程序可以使用此系统,如中所述 AttributeEvents
.
Object Name | Description |
---|---|
validates(*names, **kw) | 将方法修饰为一个或多个命名属性的“验证器”。 |
- function sqlalchemy.orm.validates(*names, **kw)
通过使用应用于映射的自定义数据类型,可以实现以适合在Python中表示数据与在数据库中表示数据之间转换数据的方式影响列值的非ORM方法
Table
元数据。这种情况在某些编码/解码样式的情况下更为常见,这种编码/解码在数据进入数据库和返回数据时都会发生;请在 扩充现有类型 .使用描述符和混合
为属性生成修改行为的更全面的方法是使用 descriptors . 这些通常在使用
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
上面的方法是可行的,但是我们可以补充更多。而我们
EmailAddress
对象将通过email
描述符并进入_email
映射属性,类级别EmailAddress.email
属性没有可用于的常规表达式语义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
,还提供在类级别使用时的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)
上面,访问
email
的实例的属性EmailAddress
将返回_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')
阅读更多关于混合动力车的信息 混合属性 .
同义词
同义词是一个映射器级构造,允许类上的任何属性“镜像”映射的另一个属性。
在最基本的意义上,同义词是一种简单的方法,可以通过附加名称使某个属性可用:
from sqlalchemy.orm import synonym 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 . 我们可以提供status
同义词@property
::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)
当使用声明性时,可以使用
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描述符。技术上,Asynonym()
能做的每件事hybrid_property
可以,因为它还支持自定义SQL功能的注入,但是在更复杂的情况下,混合更容易使用。Object Name Description synonym(name[, map_column, descriptor, comparator_factory, ...])
将属性名表示为映射属性的同义词,因为该属性将镜像另一个属性的值和表达式行为。
- function sqlalchemy.orm.synonym(name, map_column=None, descriptor=None, comparator_factory=None, doc=None, info=None)
SQLAlchemy ORM和核心表达式语言使用的“操作符”是完全可自定义的。例如,比较表达式
User.name == 'ed'
使用内置于python本身的运算符operator.eq
-可以修改sqlAlchemy与此类运算符关联的实际sql构造。新操作也可以与列表达式关联。列表达式所用的运算符在类型级别上最直接地重新定义-请参见部分 重新定义和创建新的运算符 以获取描述。ORM级功能
column_property()
,relationship()
和composite()
还通过传递PropComparator
子类到comparator_factory
每个函数的参数。这一级别的操作员定制是一个罕见的用例。参见文档PropComparator
以获取概述。