版本:1.1.0b2 |发布日期:2016年7月1日

SQLAlchemy 1.1文档

更改属性行为

简单验证器

向属性添加“验证”例程的一种快速方法是使用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内置的ValueErrorAssertionError异常是合理的选择),或者可以修改或替换之前的值诉讼。函数应返回给定的值。

请注意,集合的验证程序不能在验证例程中发出该集合的加载 - 此用法引发一个断言以避免递归溢出。这是一个不支持的重入条件。

参数:
  • *名称 - 要验证的属性名称列表。
  • include_removes -

    如果为True,“remove”事件也将被发送 - 验证函数必须接受一个额外的参数“is_remove”,它将是一个布尔值。

    New in version 0.7.7.

  • include_backrefs -

    默认为True;如果False,如果始发者是通过backref相关的属性事件,则验证函数不会发出。这可以用于双向validates()用法,其中每个属性操作只有一个验证器应该发出。

    版本0.9.0中的新功能

使用描述符和混合

为属性生成修改后行为的更全面的方法是使用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()

address.email = 'otheraddress@example.com'
sqlsession.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()

Hybrid Attributes中了解更多关于混合动力的信息。

同义词¶ T0>

同义词是映射级别的结构,它允许类的任何属性“镜像”映射的另一个属性。

从最基本的意义上说,同义词是通过附加名称提供某个特定属性的简单方法:

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)

将属性名称表示为映射属性的同义词,因为该属性将镜像另一个属性的值和表达式行为。

参数:
  • 名称 - 现有映射属性的名称。这可以引用该类上配置的任何MapperProperty的字符串名称,包括列绑定的属性和关系。
  • descriptor – a Python descriptor that will be used as a getter (and potentially a setter) when this attribute is accessed at the instance level.
  • map_column -

    if True, the synonym() construct will locate the existing named MapperProperty based on the attribute name of this synonym(), and assign it to a new attribute linked to the name of this synonym(). 也就是说,给定一个映射如下:

    class MyClass(Base):
        __tablename__ = 'my_table'
    
        id = Column(Integer, primary_key=True)
        job_status = Column(String(50))
    
        job_status = synonym("_job_status", map_column=True)

    The above class MyClass will now have the job_status Column object mapped to the attribute named _job_status, and the attribute named job_status will refer to the synonym itself. 此功能通常与descriptor参数结合使用,以便将用户定义的描述符作为现有列的“包装器”链接。

  • info -

    可选数据字典,将填充到此对象的InspectionAttr.info属性中。

    版本1.0.0中的新功能

  • comparator_factory -

    PropComparator的子类,将在SQL表达式级别提供自定义比较行为。

    注意

    对于提供重新定义属性的Python级别和SQL表达级别行为的属性的用例,请参阅Using Descriptors and Hybrids中介绍的Hybrid属性以获得更有效的技术。

也可以看看

Synonyms - 功能的例子。

Using Descriptors and Hybrids - 与同义词相比,混合为更复杂的属性包装方案提供了更好的方法。

操作员定制

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中的文档以获取概述。