反映数据库对象
A Table
对象可以被指示从数据库中已经存在的相应数据库架构对象加载关于其自身的信息。此过程称为 反射 。在最简单的情况下,您只需指定表名,即 MetaData
对象,而 autoload_with
参数::
>>> messages = Table('messages', meta, autoload_with=engine) >>> [c.name for c in messages.columns] ['message_id', 'message_name', 'date']
上面的操作将使用给定的引擎查询数据库以获取有关 messages
表,然后将生成 Column
, ForeignKey
以及与此信息相对应的其他对象, Table
对象是用python手工构建的。
当表被反射时,如果一个给定的表通过外键引用另一个表,则为一秒。 Table
对象是在 MetaData
表示连接的对象。下面,假设表格 shopping_cart_items
引用名为的表 shopping_carts
. 反映 shopping_cart_items
表的作用是 shopping_carts
还将加载表:
>>> shopping_cart_items = Table('shopping_cart_items', meta, autoload_with=engine) >>> 'shopping_carts' in meta.tables: True
这个 MetaData
有一个有趣的“单例”行为,如果您单独请求两个表, MetaData
确保只有一个 Table
对象是为每个不同的表名创建的。这个 Table
构造函数实际上返回给您已经存在的 Table
对象(如果已存在具有给定名称的对象)。如下图所示,我们可以访问已经生成的 shopping_carts
表的名称:
shopping_carts = Table('shopping_carts', meta)
当然,这是一个好主意,可以使用 autoload_with=engine
不管上面的表是什么。这样,如果表的属性尚未加载,则它们将被加载。仅当表尚未加载时,才会对其执行自动加载操作;一旦加载,就会对 Table
不会重新发出任何反射查询。
覆盖反射列
在反射表时,可以用显式值覆盖各个列;这对于指定自定义数据类型、约束(如数据库中可能未配置的主键等)非常方便:
>>> mytable = Table('mytable', meta, ... Column('id', Integer, primary_key=True), # override reflected 'id' to have primary key ... Column('mydata', Unicode(50)), # override reflected 'mydata' to be Unicode ... # additional Column objects which require no change are reflected normally ... autoload_with=some_engine)
参见
使用自定义类型和反射 -说明了上述列重写技术如何应用于具有表反射的自定义数据类型的使用。
反射视图
反射系统也可以反射视图。基本用法与表的用法相同:
my_view = Table("some_view", metadata, autoload_with=engine)
上面, my_view
是一个 Table
对象与 Column
对象,表示视图“某些视图”中每个列的名称和类型。
通常,在反射视图时希望至少有主键约束(如果不是外键的话)。视图反射不会推断这些约束。
为此,请使用“重写”技术,显式指定属于主键的一部分或具有外键约束的列:
my_view = Table("some_view", metadata, Column("view_id", Integer, primary_key=True), Column("related_thing", Integer, ForeignKey("othertable.thing_id")), autoload_with=engine )
同时反射所有表
这个 MetaData
对象还可以获取表列表并反映完整集。这是通过使用 reflect()
方法。调用后,所有定位的表都出现在 MetaData
对象的表字典:
metadata_obj = MetaData() metadata_obj.reflect(bind=someengine) users_table = metadata_obj.tables['users'] addresses_table = metadata_obj.tables['addresses']
metadata.reflect()
还提供了清除或删除数据库中所有行的简便方法:
metadata_obj = MetaData() metadata_obj.reflect(bind=someengine) for table in reversed(metadata_obj.sorted_tables): someengine.execute(table.delete())
带检查员的细颗粒反射
还提供了一个低级接口,它提供了从给定数据库加载模式、表、列和约束描述列表的后端不可知系统。这就是所谓的“检查员”::
from sqlalchemy import create_engine from sqlalchemy import inspect engine = create_engine('...') insp = inspect(engine) print(insp.get_table_names())
Object Name | Description |
---|---|
执行数据库架构检查。 |
- class sqlalchemy.engine.reflection.Inspector(bind)
当映射表的列时,使用
Table.autoload_with
的参数Table
或者Inspector.get_columns()
一种方法Inspector
,则数据类型将尽可能特定于目标数据库。这意味着,如果MySQL数据库反映了“整数”数据类型,则该类型将由sqlalchemy.dialects.mysql.INTEGER
类,它包括特定于MySQL的属性,如“display_width”。或在PostgreSQL上,是特定于PostgreSQL的数据类型,例如sqlalchemy.dialects.postgresql.INTERVAL
或sqlalchemy.dialects.postgresql.ENUM
可能会被退回。反射有一个用例,即给定的
Table
将被转移到不同的供应商数据库。为了适应这个用例,有一种技术可以在飞翔上将这些特定于供应商的数据类型转换为SQLAlChemy后端无关的数据类型的实例,例如上面的类型Integer
,Interval
和Enum
。这可以通过使用DDLEvents.column_reflect()
事件与TypeEngine.as_generic()
方法。在MySQL中给定表(之所以选择,是因为MySQL有许多特定于供应商的数据类型和选项):
CREATE TABLE IF NOT EXISTS my_table ( id INTEGER PRIMARY KEY AUTO_INCREMENT, data1 VARCHAR(50) CHARACTER SET latin1, data2 MEDIUMINT(4), data3 TINYINT(2) )
上表包括仅限MySQL的整数类型
MEDIUMINT
和TINYINT
以及一个VARCHAR
这包括仅限MySQL的CHARACTER SET
选项。如果我们正常地反映这个表,它会产生一个Table
对象,该对象将包含那些特定于MySQL的数据类型和选项:>>> from sqlalchemy import MetaData, Table, create_engine >>> mysql_engine = create_engine("mysql://scott:tiger@localhost/test") >>> metadata_obj = MetaData() >>> my_mysql_table = Table("my_table", metadata_obj, autoload_with=mysql_engine)
上面的示例将上面的表架构反映到一个新的
Table
对象。出于演示目的,我们可以使用以下命令打印特定于MySQL的“CREATE TABLE”语句CreateTable
构造:>>> from sqlalchemy.schema import CreateTable >>> print(CreateTable(my_mysql_table).compile(mysql_engine)) CREATE TABLE my_table ( id INTEGER(11) NOT NULL AUTO_INCREMENT, data1 VARCHAR(50) CHARACTER SET latin1, data2 MEDIUMINT(4), data3 TINYINT(2), PRIMARY KEY (id) )ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
上面,保留了特定于MySQL的数据类型和选项。如果我们想要一个
Table
我们可以干净利落地将其转移到另一家数据库供应商,替换特殊的数据类型sqlalchemy.dialects.mysql.MEDIUMINT
和sqlalchemy.dialects.mysql.TINYINT
使用Integer
,我们可以选择“泛化”该表上的数据类型,或者以任何我们想要的方式更改它们,方法是使用DDLEvents.column_reflect()
事件。自定义处理程序将利用TypeEngine.as_generic()
方法将上述特定于MySQL的类型对象转换为泛型对象,方法是将"type"
传递给事件处理程序的列字典条目中的条目。本词典的格式在Inspector.get_columns()
:>>> from sqlalchemy import event >>> metadata_obj = MetaData() >>> @event.listens_for(metadata_obj, "column_reflect") >>> def genericize_datatypes(inspector, tablename, column_dict): ... column_dict["type"] = column_dict["type"].as_generic() >>> my_generic_table = Table("my_table", metadata_obj, autoload_with=mysql_engine)
我们现在有了一个新的
Table
它是通用的,并且使用Integer
用于这些数据类型。例如,我们现在可以在PostgreSQL数据库上发出“CREATE TABLE”语句:>>> pg_engine = create_engine("postgresql://scott:tiger@localhost/test", echo=True) >>> my_generic_table.create(pg_engine) CREATE TABLE my_table ( id SERIAL NOT NULL, data1 VARCHAR(50), data2 INTEGER, data3 INTEGER, PRIMARY KEY (id) )
还请注意,SQLAlChemy通常会对其他行为做出不错的猜测,比如MySQL
AUTO_INCREMENT
指令在PostgreSQL中使用SERIAL
自动递增数据类型。1.4 新版功能: 添加了
TypeEngine.as_generic()
方法,并额外改进了DDLEvents.column_reflect()
事件,以便它可以应用于MetaData
为方便起见反对。反射的局限性
需要注意的是,反射过程会重新创建
Table
仅使用关系数据库中表示的信息的元数据。根据定义,此进程无法还原数据库中实际未存储的架构的各个方面。反射不可用的状态包括但不限于:客户端默认值,可以是python函数,也可以是使用
default
关键字Column
(注意,这与server_default
这是通过反射可以得到的)。列信息,例如可能放入
Column.info
词典
在许多情况下,关系数据库还以与SQLAlchemy中指定的格式不同的格式报告表元数据。这个
Table
从反射返回的对象不能总是依赖于生成与原始python定义的相同的ddlTable
物体。出现这种情况的区域包括服务器默认值、与列关联的序列以及有关约束和数据类型的各种特性。服务器端默认值可以用CAST指令返回(通常PostgreSQL将包括::<type>
或不同于最初指定的引用模式。另一类限制包括模式结构,反射仅部分定义或尚未定义。最近对反射的改进允许反射视图、索引和外键选项等内容。在撰写本文时,检查约束、表注释和触发器等结构不会反映出来。