具有声明性的表配置

优质
小牛编辑
142浏览
2023-12-01

正如在 声明性映射 ,声明式样式包括生成映射 Table 对象,或容纳 Table 或其他 FromClause 直接对象。

以下示例假定声明性基类为:

from sqlalchemy.orm import declarative_base

Base = declarative_base()

下面的所有示例都说明了从上面继承的类 Base . 装饰风格 使用修饰符的声明性映射(无声明基) 下面的所有示例也完全支持。

声明性表格

对于声明性基类,典型的映射形式包括一个属性 __tablename__ 它指示一个 Table 应该与映射一起生成:

from sqlalchemy import Column, Integer, String, ForeignKey
from sqlalchemy.orm import declarative_base

Base = declarative_base()

class User(Base):
    __tablename__ = 'user'

    id = Column(Integer, primary_key=True)
    name = Column(String)
    fullname = Column(String)
    nickname = Column(String)

上面, Column 对象与类定义内联放置。声明性映射过程将生成一个新的 Table 反对 MetaData 与声明基关联的集合,并且每个 Column 对象将成为 Table.columns 这个的集合 Table 对象。这个 Column 对象可以省略其“name”字段,该字段通常是 Column 构造函数;声明性系统将分配与每个 Column 作为名字,产生一个 Table 相当于:

# equivalent Table object produced
user_table = Table(
    "user",
    Base.metadata,
    Column("id", Integer, primary_key=True),
    Column("name", String),
    Column("fullname", String),
    Column("nickname", String),
)

访问表和元数据

声明性映射类将始终包含一个名为 __table__ ;当上述配置使用 __tablename__ 完成时,声明过程使 Table 可通过 __table__ 属性:

# access the Table
user_table = User.__table__

上表与 Mapper.local_table 属性,我们可以通过 runtime inspection system ::

from sqlalchemy import inspect

user_table = inspect(User).local_table

这个 MetaData 与声明性 registry 以及基类通常是运行DDL操作(如CREATE)以及与迁移工具(如Alembic)一起使用时所必需的。此对象可通过 .metadata 属性 registry 以及声明性基类。下面,对于一个小脚本,我们可能希望针对SQLite数据库发出一个CREATE for all tables::

engine = create_engine("sqlite://")

Base.metadata.create_all(engine)

声明性表配置

使用声明性表配置时 __tablename__ 声明类属性,要提供给 Table 构造函数应该使用 __table_args__ 声明类属性。

此属性包含通常发送到的位置参数和关键字参数 Table 构造函数。属性可以用两种形式之一指定。一本是字典:

class MyClass(Base):
    __tablename__ = 'sometable'
    __table_args__ = {'mysql_engine':'InnoDB'}

另一个是元组,其中每个参数都是位置的(通常是约束)::

class MyClass(Base):
    __tablename__ = 'sometable'
    __table_args__ = (
            ForeignKeyConstraint(['id'], ['remote_table.id']),
            UniqueConstraint('foo'),
            )

通过将最后一个参数指定为字典,可以使用上述表单指定关键字参数::

class MyClass(Base):
    __tablename__ = 'sometable'
    __table_args__ = (
            ForeignKeyConstraint(['id'], ['remote_table.id']),
            UniqueConstraint('foo'),
            {'autoload':True}
            )

类也可以指定 __table_args__ 声明性属性,以及 __tablename__ 属性,在动态样式中使用 declared_attr() 方法装饰器。参见章节 混合和自定义基类 举例说明如何经常使用。

具有声明性表的显式架构名称

的架构名称 Table 如文件所示 指定架构名称 适用于个人 Table 使用 Table.schema 争论。使用声明性表时,此选项与其他选项一样传递给 __table_args__ 字典:

class MyClass(Base):
    __tablename__ = 'sometable'
    __table_args__ = {'schema': 'some_schema'}

架构名称也可以应用于所有 Table 通过使用 MetaData.schema 参数记录在 使用元数据指定默认架构名称 . 这个 MetaData 对象可以单独构造并传递给 registry()declarative_base() ::

from sqlalchemy import MetaData
metadata_obj = MetaData(schema="some_schema")

Base = declarative_base(metadata = metadata_obj)


class MyClass(Base):
    # will use "some_schema" by default
    __tablename__ = 'sometable'

参见

指定架构名称 -在 用元数据描述数据库 文档。

添加新列

声明性表配置允许添加新的 Column 两种情况下的对象。最基本的是简单地分配新的 Column 类的对象:

MyClass.some_new_column = Column('data', Unicode)

对使用声明性基(注意,不是声明性的decorator形式)映射的声明性类执行的上述操作将添加上述内容 ColumnTable 使用 Table.append_column() 方法,并将列添加到 Mapper 完全映射。

注解

将新列分配给现有的声明性映射类只有在使用“声明性基”类时才能正常工作,这也提供了元类驱动 __setattr__() 方法来截获这些操作。会的 not 如果 registry.mapped() 它也不适用于由映射的强制映射类 registry.map_imperatively() .

另一种情况是 Column 动态添加是指没有自己表的继承子类指示其他列;这些列将被添加到超类表中。部门 单表继承 讨论单表继承。

带命令表的声明性(又称混合声明性)

声明性映射也可以与预先存在的 Table 对象,或 Table 或者其他武断的 FromClause 构造(例如 JoinSubquery )它是分开建造的。

这被称为“混合声明性”映射,因为类是使用声明式样式映射涉及映射器配置的所有内容,但是映射 Table 对象单独生成并直接传递给声明性进程:

from sqlalchemy.orm import declarative_base
from sqlalchemy import Column, Integer, String, ForeignKey


Base = declarative_base()

# construct a Table directly.  The Base.metadata collection is
# usually a good choice for MetaData but any MetaData
# collection may be used.

user_table = Table(
    "user",
    Base.metadata,
    Column("id", Integer, primary_key=True),
    Column("name", String),
    Column("fullname", String),
    Column("nickname", String),
)

# construct the User class using this table.
class User(Base):
    __table__ = user_table

以上,A Table 对象是使用中描述的方法构造的 用元数据描述数据库 . 然后可以直接将其应用于声明性映射的类。这个 __tablename____table_args__ 此表单中未使用声明性类属性。作为内联定义,上述配置通常更具可读性:

class User(Base):
    __table__ = Table(
        "user",
        Base.metadata,
        Column("id", Integer, primary_key=True),
        Column("name", String),
        Column("fullname", String),
        Column("nickname", String),
    )

上述风格的自然效果是 __table__ 属性本身在类定义块中定义。因此,可以在随后的属性中立即引用它,例如下面的示例说明了对 type 多态映射器配置中的列::

class Person(Base):
    __table__ = Table(
        'person',
        Base.metadata,
        Column('id', Integer, primary_key=True),
        Column('name', String(50)),
        Column('type', String(50))
    )

    __mapper_args__ = {
        "polymorphic_on": __table__.c.type,
        "polymorhpic_identity": "person"
    }

当非 -Table 构造,例如 JoinSubquery 对象,将被映射。下面是一个例子:

from sqlalchemy import select, func

subq = select(
    func.count(orders.c.id).label('order_count'),
    func.max(orders.c.price).label('highest_order'),
    orders.c.customer_id
).group_by(orders.c.customer_id).subquery()

customer_select = select(customers, subq).join_from(
    customers, subq, customers.c.id == subq.c.customer_id
).subquery()

class Customer(Base):
    __table__ = customer_select

关于映射到非 -Table 构件见截面 根据多个表映射类针对任意子查询映射类 .

当类本身使用属性声明的替代形式(如Python数据类)时,“命令表”形式特别有用。参见章节 具有数据类和属性的声明性映射 细节。

参见

用元数据描述数据库

具有数据类和属性的声明性映射

用反射表声明性地映射

有几种可用的模式提供了针对一系列 Table 使用中描述的反射过程从数据库中内省的对象 反映数据库对象 .

将类映射到从数据库反映的表的一种非常简单的方法是使用声明性混合映射,传递 Table.autoload_with 参数 Table ::

engine = create_engine("postgresql://user:pass@hostname/my_existing_database")

class MyClass(Base):
    __table__ = Table(
        'mytable',
        Base.metadata,
        autoload_with=engine
    )

然而,上述方法的一个主要缺点是,它要求在声明应用程序类时存在数据库连接源;通常情况下,类声明为应用程序的模块被导入,但是数据库连接在应用程序开始运行代码之前是不可用的,这样它就可以使用配置信息并创建引擎。

使用DeferredReflection

为了适应这种情况,一个名为 DeferredReflection mixin是可用的,它将声明性映射过程延迟到一个特殊的类级别 DeferredReflection.prepare() 方法,该方法将对目标数据库执行反射过程,并将结果与声明性表映射过程(即使用 __tablename__ 属性:

from sqlalchemy.orm import declarative_base
from sqlalchemy.ext.declarative import DeferredReflection

Base = declarative_base()

class Reflected(DeferredReflection):
    __abstract__ = True

class Foo(Reflected, Base):
    __tablename__ = 'foo'
    bars = relationship("Bar")

class Bar(Reflected, Base):
    __tablename__ = 'bar'

    foo_id = Column(Integer, ForeignKey('foo.id'))

上面,我们创建了一个mixin类 Reflected 它将作为声明性层次结构中的类的基,当 Reflected.prepare 方法被调用。在我们这样做之前,上面的映射是不完整的 Engine ::

engine = create_engine("postgresql://user:pass@hostname/my_existing_database")
Reflected.prepare(engine)

目的 Reflected 类定义类的反射映射范围。插件将在目标的子类树中搜索 .prepare() 调用并反映所有表。

使用自动映射

针对要使用表反射的现有数据库进行映射的更自动化的解决方案是使用 自动程序 扩展。这个扩展将从数据库模式生成完整的映射类,并允许几个钩子进行自定义,包括显式映射某些或所有类,同时仍然使用反射来填充其余列。

参见

自动程序