SqlAlchemy 2.0 中文文档(八)(3)

简介: SqlAlchemy 2.0 中文文档(八)

SqlAlchemy 2.0 中文文档(八)(2)https://developer.aliyun.com/article/1559847


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

通过使用应用于映射的 Table 元数据的自定义数据类型,可以以适合在 Python  中的表示方式与在数据库中的表示方式之间转换数据的方式来影响列的值的非 ORM  方法。这在某些编码/解码样式在数据进入数据库和返回时都发生的情况下更为常见;在核心文档中阅读更多关于此的内容,参见扩充现有类型。

使用描述符和混合类型

产生修改后的属性行为的更全面的方法是使用描述符。在 Python 中,通常使用 property() 函数来使用这些。描述符的标准 SQLAlchemy 技术是创建一个普通描述符,并从具有不同名称的映射属性读取/写入。下面我们使用 Python 2.6 风格的属性来说明这一点:

class EmailAddress(Base):
    __tablename__ = "email_address"
    id = mapped_column(Integer, primary_key=True)
    # name the attribute with an underscore,
    # different from the column name
    _email = mapped_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属性不具有通常可用于Select的表达语义。为了提供这些功能,我们使用 hybrid 扩展,如下所示:

from sqlalchemy.ext.hybrid import hybrid_property
class EmailAddress(Base):
    __tablename__ = "email_address"
    id = mapped_column(Integer, primary_key=True)
    _email = mapped_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
from sqlalchemy import select
session = Session()
address = session.scalars(
    select(EmailAddress).where(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"
session.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 = mapped_column(Integer, primary_key=True)
    _email = mapped_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 函数:

address = session.scalars(
    select(EmailAddress).where(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 = mapped_column(Integer, primary_key=True)
    job_status = mapped_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()还可以引用用户定义的描述符。我们可以用@property来提供我们的status同义词:

class MyClass(Base):
    __tablename__ = "my_table"
    id = mapped_column(Integer, primary_key=True)
    status = mapped_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 = mapped_column(Integer, primary_key=True)
    status = mapped_column(String(50))
    @synonym_for("status")
    @property
    def job_status(self):
        return "Status: " + self.status

虽然synonym()对于简单的镜像很有用,但是使用描述符增强属性行为的用例更好地在现代使用中使用混合属性特性来处理,后者更加面向 Python 描述符。从技术上讲,一个synonym()可以做任何一个hybrid_property能做的事情,因为它也支持注入自定义 SQL 功能,但是在更复杂的情况下混合属性更容易使用。

对象名称 描述
synonym(name, *, [map_column, descriptor, comparator_factory, init, repr, default, default_factory, compare, kw_only, info, doc]) 将一个属性名表示为映射属性的同义词,即该属性将反映另一个属性的值和表达式行为。
function sqlalchemy.orm.synonym(name: str, *, map_column: bool | None = None, descriptor: Any | None = None, comparator_factory: Type[PropComparator[_T]] | None = None, init: _NoArg | bool = _NoArg.NO_ARG, repr: _NoArg | bool = _NoArg.NO_ARG, default: _NoArg | _T = _NoArg.NO_ARG, default_factory: _NoArg | Callable[[], _T] = _NoArg.NO_ARG, compare: _NoArg | bool = _NoArg.NO_ARG, kw_only: _NoArg | bool = _NoArg.NO_ARG, info: _InfoType | None = None, doc: str | None = None) → Synonym[Any]

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

例如:

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 描述符,当访问此属性时将用作 getter(和可能的 setter)。
  • map_column
    仅适用于传统映射和对现有表对象的映射。如果为True,则synonym()构造将定位到在此同义词的属性名称通常与该同义词的属性名称相关联的映射表上的Column对象,并生成一个新的ColumnProperty,该属性将此Column映射到作为同义词的“name”参数给定的替代名称;通过这种方式,重新定义Column的映射为不同名称的步骤是不必要的。这通常用于当Column要被替换为也使用描述符的属性时,也就是与synonym.descriptor参数结合使用时:
my_table = Table(
    "my_table", metadata,
    Column('id', Integer, primary_key=True),
    Column('job_status', String(50))
)
class MyClass:
    @property
    def _job_status_descriptor(self):
        return "Status: %s" % self._job_status
mapper(
    MyClass, my_table, properties={
        "job_status": synonym(
            "_job_status", map_column=True,
            descriptor=MyClass._job_status_descriptor)
    }
)
  • 在上面的例子中,名为_job_status的属性会自动映射到job_status列:
>>> j1 = MyClass()
>>> j1._job_status = "employed"
>>> j1.job_status
Status: employed
  • 当使用声明式时,为了与同义词结合使用提供描述符,请使用sqlalchemy.ext.declarative.synonym_for()助手。但是,请注意,通常应优选混合属性功能,特别是在重新定义属性行为时。
  • info – 可选的数据字典,将填充到此对象的InspectionAttr.info属性中。
  • comparator_factory
    PropComparator的子类,将在 SQL 表达式级别提供自定义比较行为。
    注意
    对于提供重新定义属性的 Python 级别和 SQL 表达式级别行为的用例,请参阅使用描述符和混合中介绍的混合属性,这是一种更有效的技术。

另请参阅

同义词 - 同义词概述

synonym_for() - 一种面向声明式的辅助工具

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

运算符定制

SQLAlchemy ORM 和 Core 表达式语言使用的“运算符”是完全可定制的。例如,比较表达式 User.name == 'ed' 使用了 Python 本身内置的名为 operator.eq 的运算符 - SQLAlchemy 关联的实际 SQL 构造可以被修改。新的操作也可以与列表达式关联起来。最直接重新定义列表达式的运算符的方法是在类型级别进行 - 详细信息请参阅重新定义和创建新的运算符。

ORM 级别的函数如column_property()relationship()composite()还提供了在 ORM 级别重新定义运算符的功能,方法是将PropComparator子类传递给每个函数的comparator_factory参数。在这个级别定制运算符的情况很少见。详细信息请参阅PropComparator的文档概述。


SqlAlchemy 2.0 中文文档(八)(4)https://developer.aliyun.com/article/1559851

相关文章
|
2天前
|
SQL JSON 关系型数据库
SqlAlchemy 2.0 中文文档(二)(2)
SqlAlchemy 2.0 中文文档(二)
15 2
|
2天前
|
SQL 测试技术 Python
SqlAlchemy 2.0 中文文档(二)(1)
SqlAlchemy 2.0 中文文档(二)
13 2
|
2天前
|
SQL 自然语言处理 数据库
SqlAlchemy 2.0 中文文档(二)(3)
SqlAlchemy 2.0 中文文档(二)
10 2
|
2天前
|
SQL JSON 关系型数据库
SqlAlchemy 2.0 中文文档(二)(4)
SqlAlchemy 2.0 中文文档(二)
11 2
|
2天前
|
存储 Python
SqlAlchemy 2.0 中文文档(七)(5)
SqlAlchemy 2.0 中文文档(七)
10 1
|
2天前
|
SQL 存储 测试技术
SqlAlchemy 2.0 中文文档(九)(2)
SqlAlchemy 2.0 中文文档(九)
12 0
|
2天前
|
Python
SqlAlchemy 2.0 中文文档(九)(3)
SqlAlchemy 2.0 中文文档(九)
8 0
|
2天前
|
测试技术 API 数据库
SqlAlchemy 2.0 中文文档(九)(4)
SqlAlchemy 2.0 中文文档(九)
7 0
|
2天前
|
SQL 数据库 Python
SqlAlchemy 2.0 中文文档(六)(2)
SqlAlchemy 2.0 中文文档(六)
6 0
|
2天前
|
SQL 前端开发 数据库
SqlAlchemy 2.0 中文文档(六)(1)
SqlAlchemy 2.0 中文文档(六)
13 0