随着引擎和SQL执行的停止,我们准备开始一些炼金术。SQLAlchemy Core和ORM的核心元素是SQL表达式语言,它允许流畅、可组合地构造SQL查询。这些查询的基础是表示数据库概念(如表和列)的Python对象。这些对象统称为 database metadata .
SQLAlchemy中数据库元数据最常见的基本对象是 MetaData
, Table
和 Column
. 下面的部分将说明如何在面向核心的样式和面向ORM的样式中使用这些对象。
ORM读者们,跟我们呆在一起!
与其他部分一样,核心用户可以跳过ORM部分,但是ORM用户最好从两个角度熟悉这些对象。
当我们使用关系数据库时,我们创建和查询的基本结构称为 桌子 . 在SQLAlchemy中,“table”由一个类似于 Table
.
要开始使用SQLAlchemy表达式语言,我们需要 Table
对象,这些对象表示我们感兴趣的所有数据库表。每个 Table
可能是 宣布 ,这意味着我们在源代码中明确说明了表的外观或可能是什么 反射 ,这意味着我们根据特定数据库中已经存在的内容生成对象。这两种方法也可以在许多方面混合使用。
无论我们将声明还是反映我们的表,我们从一个集合开始,这个集合将是我们放置表的地方,称为 MetaData
对象。这个对象本质上是一个 facade 在Python字典中存储了一系列 Table
对象键控到其字符串名称。构造此对象的方式如下:
>>> from sqlalchemy import MetaData
>>> metadata_obj = MetaData()
有一间单人房 MetaData
对象用于整个应用程序是最常见的情况,表示为应用程序中单个位置的模块级变量,通常是在“模型”或“dbschema”类型的包中。可以有多个 MetaData
集合也是如此,但是,如果一系列 Table
相互关联的对象属于单个 MetaData
收藏。
一旦我们拥有了 MetaData
对象,我们可以声明一些 Table
物体。本教程将从经典的SQLAlchemy教程模型开始,即表的模型 user
,例如,它将表示网站的用户和表 address
,表示与中的行关联的电子邮件地址列表 user
表。我们通常分配每个 Table
对象,该变量将是我们在应用程序代码中引用表的方式:
>>> from sqlalchemy import Table, Column, Integer, String
>>> user_table = Table(
... "user_account",
... metadata_obj,
... Column('id', Integer, primary_key=True),
... Column('name', String(30)),
... Column('fullname', String)
... )
我们可以观察到 Table
construct看起来很像SQL CREATE TABLE语句;从表名开始,然后列出每列,其中每列都有一个名称和一个数据类型。我们上面使用的对象是:
Column
-表示数据库表中的列,并将其自身分配给 Table
对象。这个 Column
通常包括一个字符串名和一个类型对象。收藏 Column
对象的父对象 Table
通常通过位于 Table.c
::
>>> user_table.c.name
Column('name', String(length=30), table=<user_account>)
>>> user_table.c.keys()
['id', 'name', 'fullname']
Integer
, String
-这些类表示SQL数据类型,可以传递给 Column
无论是否需要实例化。上面,我们想给“name”列指定一个“30”的长度,因此我们实例化了 String(30)
. 但是对于“id”和“fullname”,我们没有指定这些,所以我们可以发送类本身。
第一 Column
在上面 user_table
包括 Column.primary_key
参数,它是指示 Column
应该是此表的主键的一部分。主键本身通常隐式声明,并由 PrimaryKeyConstraint
我们可以在 Table.primary_key
属性 Table
对象:
>>> user_table.primary_key
PrimaryKeyConstraint(Column('id', Integer(), table=<user_account>, primary_key=True, nullable=False))
最典型的显式声明的约束是 ForeignKeyConstraint
对象与数据库相对应 foreign key constraint . 当我们声明相互关联的表时,SQLAlchemy使用这些外键约束声明的存在,不仅使它们在CREATE语句中发送到数据库,而且还帮助构造SQL表达式。
A ForeignKeyConstraint
它只涉及目标表上的一个列,通常通过 ForeignKey
对象。下面我们宣布第二张桌子 address
将有一个外键约束引用 user
表:
>>> from sqlalchemy import ForeignKey
>>> address_table = Table(
... "address",
... metadata_obj,
... Column('id', Integer, primary_key=True),
... Column('user_id', ForeignKey('user_account.id'), nullable=False),
... Column('email_address', String, nullable=False)
... )
上面的第三个约束条件中也使用了“非空”约束 Column.nullable
参数。
小技巧
当使用 ForeignKey
对象 Column
定义,我们可以省略该数据类型 Column
;它是从相关列的值自动推断出来的,在上面的示例中 Integer
的数据类型 user_account.id
列。
在下一节中,我们将为 user
和 address
表以查看完成的结果。
我们构建了一个相当复杂的对象层次结构来表示两个数据库表,从根开始 MetaData
对象,然后分成两部分 Table
对象,每个对象都包含 Column
和 Constraint
物体。随着Core和ORM的进一步发展,这个对象结构将成为我们执行的大多数操作的中心。
我们可以用这个结构做的第一件有用的事情是发出createtable语句,或者 DDL ,以插入和查询来自它们的数据。通过调用 MetaData.create_all()
我们的方法 MetaData
,发送 Engine
引用目标数据库:
>>> metadata_obj.create_all(engine)
BEGIN (implicit)
PRAGMA main.table_...info("user_account")
...
PRAGMA main.table_...info("address")
...
CREATE TABLE user_account (
id INTEGER NOT NULL,
name VARCHAR(30),
fullname VARCHAR,
PRIMARY KEY (id)
)
...
CREATE TABLE address (
id INTEGER NOT NULL,
user_id INTEGER NOT NULL,
email_address VARCHAR NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY(user_id) REFERENCES user_account (id)
)
...
COMMIT
默认情况下,DDL create进程包含一些SQLite特定的PRAGMA语句,这些语句在发出create之前测试每个表是否存在。在BEGIN/COMMIT对中还包含了完整的一系列步骤,以适应事务性DDL(SQLite实际上支持事务性DDL,但是 sqlite3
数据库驱动程序在“自动提交”模式下运行DDL)。
create进程还负责按照正确的顺序发出create语句;上面,外键约束依赖于 user
表已存在,因此 address
然后创建表。在更复杂的依赖场景中,外键约束也可以在使用ALTER之后应用于表。
这个 MetaData
对象还具有 MetaData.drop_all()
方法,该方法将以与为删除架构元素而发出CREATE相反的顺序发出DROP语句。
仅限ORM的这一节将提供一个示例,使用更加以ORM为中心的配置范例声明与上一节中说明的相同的数据库结构。使用ORM时,我们声明的过程 Table
元数据通常与声明过程相结合 mapped 上课。映射的类是我们想要创建的任何Python类,然后它将具有链接到数据库表中的列的属性。虽然有几种不同的实现方式,但最常见的方式是 declarative ,并允许我们声明用户定义的类和 Table
一次使用元数据。
使用ORM时 MetaData
集合仍然存在,但是它本身包含在名为 registry
. 我们创建一个 registry
通过构造它:
>>> from sqlalchemy.orm import registry
>>> mapper_registry = registry()
以上 registry
,在构造时,自动包含 MetaData
对象,该对象将存储 Table
物体::
>>> mapper_registry.metadata
MetaData()
而不是声明 Table
对象,现在我们将通过应用于映射类的指令间接声明它们。在最常见的方法中,每个映射的类都从一个称为 陈述性基础 . 我们从 registry
使用 registry.generate_base()
方法:
>>> Base = mapper_registry.generate_base()
小技巧
创建 registry
“声明性基”类可以使用历史上熟悉的 declarative_base()
功能:
from sqlalchemy.orm import declarative_base
Base = declarative_base()
这个 Base
上面的对象是一个Python类,它将作为我们声明的ORM映射类的基类。我们现在可以为 user
和 address
新课程表 User
和 Address
::
>>> from sqlalchemy.orm import relationship
>>> class User(Base):
... __tablename__ = 'user_account'
...
... id = Column(Integer, primary_key=True)
... name = Column(String(30))
... fullname = Column(String)
...
... addresses = relationship("Address", back_populates="user")
...
... def __repr__(self):
... return f"User(id={self.id!r}, name={self.name!r}, fullname={self.fullname!r})"
>>> class Address(Base):
... __tablename__ = 'address'
...
... id = Column(Integer, primary_key=True)
... email_address = Column(String, nullable=False)
... user_id = Column(Integer, ForeignKey('user_account.id'))
...
... user = relationship("User", back_populates="addresses")
...
... def __repr__(self):
... return f"Address(id={self.id!r}, email_address={self.email_address!r})"
以上两个类现在是我们的映射类,可以在ORM持久性和查询操作中使用,这将在后面介绍。但它们也包括 Table
对象是作为声明性映射过程的一部分生成的,并且与我们在前面的核心部分中直接声明的对象等效。我们可以看到这些 Table
使用 .__table__
属性:
>>> User.__table__
Table('user_account', MetaData(),
Column('id', Integer(), table=<user_account>, primary_key=True, nullable=False),
Column('name', String(length=30), table=<user_account>),
Column('fullname', String(), table=<user_account>), schema=None)
这个 Table
对象是从基于 .__tablename__
属性,以及通过使用 Column
为类中的类级别属性指定的对象。这些 Column
对象通常可以在构造函数中没有显式的“name”字段来声明,因为声明性进程将根据使用的属性名自动命名它们。
参见
声明性映射 -声明性类映射概述
要快速解释上述类,请注意以下属性:
the classes have an automatically generated __init__() method -默认情况下,两个类都接收 __init__()
方法,该方法允许对象的参数化构造。我们可以自由提供我们自己的 __init__()
方法也是。这个 __init__()
允许我们创建 User
和 Address
传递属性名,上面大部分属性名直接链接到 Column
对象,作为参数名:
>>> sandy = User(name="sandy", fullname="Sandy Cheeks")
有关此方法的详细信息,请访问 缺省构造 .
we provided a __repr__() method -这是 完全可选 ,并且是严格的,以便我们的自定义类具有描述性字符串表示形式,并且在其他情况下不需要:
>>> sandy
User(id=None, name='sandy', fullname='Sandy Cheeks')
上面有一件有趣的事需要注意的是 id
属性自动返回 None
而不是升高 AttributeError
对于缺少属性的Python通常的行为也是如此。
我们还包括一个双向关系 -这是另一个 完全可选 在这里我们使用了一个名为 relationship()
在两个类上,这向ORM指示 User
和 Address
类在 one to many / many to one 关系。使用 relationship()
我们可以在后面的教程中演示它的行为 不需要的 为了定义 Table
结构。
此节的名称与节的名称相同 向数据库发送DDL 就核心问题进行了讨论。这是因为用我们的ORM映射类发出DDL没有什么不同。如果我们想为 Table
作为声明性映射类的一部分创建的对象,我们仍然可以使用 MetaData.create_all()
像以前一样。
在我们的例子中,我们已经生成了 user
和 address
SQLite数据库中的表。如果我们还没有这样做,我们就可以自由地利用 MetaData
与我们的 registry
和ORM声明性基类,使用 MetaData.create_all()
::
# emit CREATE statements given ORM registry
mapper_registry.metadata.create_all(engine)
# the identical MetaData object is also present on the
# declarative base
Base.metadata.create_all(engine)
作为之前在 声明映射类 ,我们也可以利用 Table
我们直接在节中创建的对象 使用表对象设置元数据 与来自 declarative_base()
生成的基类。
这种形式称为 hybrid table ,它包括分配给 .__table__
属性,而不是让声明性进程生成它:
class User(Base):
__table__ = user_table
addresses = relationship("Address", back_populates="user")
def __repr__(self):
return f"User({self.name!r}, {self.fullname!r})"
class Address(Base):
__table__ = address_table
user = relationship("User", back_populates="addresses")
def __repr__(self):
return f"Address({self.email_address!r})"
上面两个类与我们在前面的映射示例中声明的类是等价的。
传统的“声明性基础”方法使用 __tablename__
自动生成 Table
对象仍然是声明表元数据的最常用方法。然而,不管它实现的ORM映射功能,就表声明而言,它只是在 Table
建造师。
接下来,当我们在本节中讨论ORM的数据操作时,我们将参考上面的ORM映射类 使用ORM插入行 .
为了完成关于使用表元数据的部分,我们将演示在本节开头提到的另一个操作,即 桌子反射 . 表反射是指生成 Table
通过读取数据库的当前状态。而在前面的章节中我们已经声明 Table
对象,然后将DDL发送到数据库,反射过程则相反。
作为反射的示例,我们将创建一个新的 Table
对象,该对象表示 some_table
我们在本文档前面几节中手动创建的对象。执行此操作的方式也有一些变化,但最基本的是构造 Table
对象,给定表的名称和一个 MetaData
集合,而不是指示单个 Column
和 Constraint
对象,则将其传递给目标 Engine
使用 Table.autoload_with
参数:
>>> some_table = Table("some_table", metadata_obj, autoload_with=engine)
BEGIN (implicit)
PRAGMA main.table_...info("some_table")
[raw sql] ()
SELECT sql FROM (SELECT * FROM sqlite_master UNION ALL SELECT * FROM sqlite_temp_master) WHERE name = ? AND type = 'table'
[raw sql] ('some_table',)
PRAGMA main.foreign_key_list("some_table")
...
PRAGMA main.index_list("some_table")
...
ROLLBACK
在过程结束时, some_table
对象现在包含有关 Column
对象,并且该对象的使用方式与 Table
我们明确声明::
>>> some_table
Table('some_table', MetaData(),
Column('x', INTEGER(), table=<some_table>),
Column('y', INTEGER(), table=<some_table>),
schema=None)
SQLAlchemy 1.4 / 2.0 Tutorial
下一个教程部分: 使用数据
flambé! the dragon and The Alchemist image designs created and generously donated by Rotem Yaari.
Created using Sphinx 4.2.0.