Release: 1.4.25 | Release Date: September 22, 2021

SQLAlchemy 1.4 Documentation

更改属性行为

简单验证器

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

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

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

  • include_removes -- 如果为真,也将发送“移除”事件-验证函数必须接受一个附加参数“is_remove”,该参数将是布尔值。

  • include_backrefs -- 默认为 True 如果 False ,如果发起方是通过backref关联的属性事件,则验证函数将不会发出。这可用于双向 validates() 用法:每个属性操作只应发出一个验证器。…添加的版本:0.9.0

参见

简单验证器 - usage examples for validates()

在核心级别使用自定义数据类型

通过使用应用于映射的自定义数据类型,可以实现以适合在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()

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)

上面,访问 email 的实例的属性 EmailAddress 将返回 _email 属性,删除或添加主机名 @example.com 从值开始。当我们对 email 属性,将呈现产生相同效果的SQL函数:

sqladdress = session.query(EmailAddress).filter(EmailAddress.email == 'address').one()

阅读更多关于混合动力车的信息 混合属性 .

同义词

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

在最基本的意义上,同义词是一种简单的方法,可以通过附加名称使某个属性可用:

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描述符。技术上,A synonym() 能做的每件事 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)

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

例如。::

class MyClass(Base):
    __tablename__ = 'my_table'

    id = Column(Integer, primary_key=True)
    job_status = Column(String(50))

    status = synonym("job_status")
参数
  • name -- 现有映射属性的名称。这可以引用在类上配置的字符串名称ORM映射属性,包括列绑定属性和关系。

  • descriptor -- Python descriptor 当在实例级别访问此属性时,它将用作getter(可能是setter)。

  • map_column -- 仅用于针对现有表对象的经典映射和映射 . 如果 True , the synonym() 构造将定位 Column 映射表上通常与该同义词的属性名关联的对象,并生成一个新的 ColumnProperty 而是映射这个 Column 作为同义词的“name”参数给出的备用名称;这样,重新定义 Column 换个名字是不必要的。通常用于以下情况: Column 将替换为也使用描述符的属性,即,与 synonym.descriptor 参数::my_table=table(“my_table”,元数据,列(“id”,integer,primary_key=true),列(“job_status”,string(50)))类my class(object):@property def _job_status_descriptor(self):return“状态:%s%”%self._job_status mapper(my class,my_table,properties=”job_status:上面的同义词(“作业状态”,map_column=true,descriptor=myclass.u作业状态描述符)),名为 _job_status 自动映射到 job_status 列::>>>j1=myclass()>>>j1.job_status=“employed”>>>j1.job_status status:使用声明性时使用,为了提供与同义词一起使用的描述符,请使用 sqlalchemy.ext.declarative.synonym_for() 帮手。但是,请注意 hybrid properties 特性通常是首选的,特别是在重新定义属性行为时。

  • info -- 可选数据字典,将填充到 InspectionAttr.info 此对象的属性。…添加的版本:1.0.0

  • comparator_factory -- 一个子类 PropComparator 这将提供SQL表达式级别的自定义比较行为。…注意::有关提供属性的用例,该属性定义了属性的python级别和sql表达式级别行为,请参阅在中介绍的混合属性。 使用描述符和混合 为了更有效的技术。

参见

同义词 -同义词概述

synonym_for() -面向声明性的助手

使用描述符和混合 -混合属性扩展提供了一种比同义词更灵活地增强属性行为的更新方法。

操作员自定义

SQLAlchemy ORM和核心表达式语言使用的“操作符”是完全可自定义的。例如,比较表达式 User.name == 'ed' 使用内置于python本身的运算符 operator.eq -可以修改sqlAlchemy与此类运算符关联的实际sql构造。新操作也可以与列表达式关联。列表达式所用的运算符在类型级别上最直接地重新定义-请参见部分 重新定义和创建新的运算符 以获取描述。

ORM级功能 column_property()relationship()composite() 还通过传递 PropComparator 子类到 comparator_factory 每个函数的参数。这一级别的操作员定制是一个罕见的用例。参见文档 PropComparator 以获取概述。

Previous: 作为映射属性的SQL表达式 Next: 组合列类型