SqlAlchemy 2.0 中文文档(三十)(1)https://developer.aliyun.com/article/1562435
使用明确声明的自动映射
正如前面所述,自动映射不依赖于反射,并且可以利用MetaData 集合内的任何 Table 对象集合。由此可见,自动映射也可以在完全定义了表元数据的完整模型中生成丢失的关系:
from sqlalchemy.ext.automap import automap_base from sqlalchemy import Column, Integer, String, ForeignKey Base = automap_base() class User(Base): __tablename__ = "user" id = Column(Integer, primary_key=True) name = Column(String) class Address(Base): __tablename__ = "address" id = Column(Integer, primary_key=True) email = Column(String) user_id = Column(ForeignKey("user.id")) # produce relationships Base.prepare() # mapping is complete, with "address_collection" and # "user" relationships a1 = Address(email="u1") a2 = Address(email="u2") u1 = User(address_collection=[a1, a2]) assert a1.user is u1
在上面的例子中,对于大部分完成的 User 和 Address 映射,我们在 Address.user_id 上定义的 ForeignKey 允许在映射的类上生成一个双向关系对 Address.user 和 User.address_collection。
注意,当子类化AutomapBase时,需要调用AutomapBase.prepare()方法;如果不调用,我们声明的类处于未映射状态。
拦截列定义
MetaData 和 Table 对象支持一个事件钩子DDLEvents.column_reflect(),可用于拦截关于数据库列反射的信息,在构建Column对象之前。例如,如果我们想要使用类似"attr_"的命名约定来映射列,可以应用该事件:
@event.listens_for(Base.metadata, "column_reflect") def column_reflect(inspector, table, column_info): # set column.key = "attr_<lower_case_name>" column_info["key"] = "attr_%s" % column_info["name"].lower() # run reflection Base.prepare(autoload_with=engine)
从版本 1.4.0b2 开始:DDLEvents.column_reflect()事件可以应用于MetaData对象。
另请参阅
DDLEvents.column_reflect()
自动从反射表中命名列 - 在 ORM 映射文档中
API 参考
| 对象名称 | 描述 |
| automap_base([declarative_base], **kw) | 生成一个声明式自动映射基类。 |
| AutomapBase | 用于“自动映射”模式的基类。 |
| classname_for_table(base, tablename, table) | 返回应用于给定表名的类名。 |
| generate_relationship(base, direction, return_fn, attrname, …, **kw) | 代表两个映射类生成一个relationship()或backref()。 |
| name_for_collection_relationship(base, local_cls, referred_cls, constraint) | 返回应用于从一个类到另一个类的集合引用的属性名称。 |
| name_for_scalar_relationship(base, local_cls, referred_cls, constraint) | 返回应用于标量对象引用的一个类到另一个类的属性名称。 |
function sqlalchemy.ext.automap.automap_base(declarative_base: Type[Any] | None = None, **kw: Any) → Any
生成一个声明式自动映射基类。
此函数生成一个新的基类,它是 AutomapBase 类的产品,以及由 declarative_base() 生成的一个声明基类。
除了 declarative_base 外,所有参数都是直接传递给 declarative_base() 函数的关键字参数。
参数:
declarative_base– 由declarative_base()生成的现有类。当传递了这个参数时,函数不再调用declarative_base()本身,所有其他关键字参数都会被忽略。**kw– 关键字参数被传递给declarative_base()。
class sqlalchemy.ext.automap.AutomapBase
用于“自动映射”模式的基类。
AutomapBase 类可以与由 declarative_base() 函数生成的“声明基类”类相比较。实际上,AutomapBase 类总是与实际的声明基类一起使用作为一个 mixin。
一个新的可子类化的 AutomapBase 通常使用 automap_base() 函数实例化。
成员
by_module, classes, metadata, prepare()
另请参阅
自动映射
attribute by_module: ClassVar[ByModuleProperties]
一个包含点分隔的模块名称层次结构链接到类的 Properties 实例。
这个集合是一个替代 AutomapBase.classes 集合的选择,当使用 AutomapBase.prepare.modulename_for_table 参数时,这个参数将为生成的类应用不同的 __module__ 属性。
自动映射生成的类的默认 __module__ 是 sqlalchemy.ext.automap;使用 AutomapBase.by_module 访问这个命名空间会像这样:
User = Base.by_module.sqlalchemy.ext.automap.User
如果一个类的 __module__ 是 mymodule.account,访问这个命名空间会像这样:
MyClass = Base.by_module.mymodule.account.MyClass
新特性在版本 2.0 中添加。
另请参阅
从多个模式生成映射
attribute classes: ClassVar[Properties[Type[Any]]]
包含类的 Properties 实例。
这个对象的行为类似于表上的 .c 集合。类以它们被赋予的名称呈现,例如:
Base = automap_base() Base.prepare(autoload_with=some_engine) User, Address = Base.classes.User, Base.classes.Address
对于类名与 Properties 方法名重叠的情况,比如 items(),也支持获取项的形式:
Item = Base.classes["items"]
attribute metadata: ClassVar[MetaData]
指的是将用于新 Table 对象的 MetaData 集合。
另请参见
访问表和元数据
classmethod prepare(autoload_with: Engine | None = None, engine: Any | None = None, reflect: bool = False, schema: str | None = None, classname_for_table: PythonNameForTableType | None = None, modulename_for_table: PythonNameForTableType | None = None, collection_class: Any | None = None, name_for_scalar_relationship: NameForScalarRelationshipType | None = None, name_for_collection_relationship: NameForCollectionRelationshipType | None = None, generate_relationship: GenerateRelationshipType | None = None, reflection_options: Dict[_KT, _VT] | immutabledict[_KT, _VT] = {}) → None
从 MetaData 中提取映射类和关系,并执行映射。
有关完整文档和示例,请参阅 基本用法。
参数:
autoload_with– 用于执行模式反射的Engine或Connection;当指定时,MetaData.reflect()方法将在此方法的范围内调用。engine–
旧版;如果AutomapBase.reflect为 True,则用于指示反映表的Engine或Connection。
自 1.4 版开始弃用:AutomapBase.prepare.engine参数已弃用,并将在未来版本中移除。请使用AutomapBase.prepare.autoload_with参数。reflect–
旧版;如果MetaData.reflect()应被调用,则使用AutomapBase.autoload_with。
自 1.4 版开始弃用:AutomapBase.prepare.reflect参数已弃用,并将在未来版本中移除。当传递AutomapBase.prepare.autoload_with时,将启用反射。classname_for_table– 可调用函数,用于根据表名生成新类名。默认为classname_for_table()。modulename_for_table–__module__的有效值将由可调用函数产生,用于为内部生成的类生成模块名,以允许在单个自动映射基类中具有相同名称的多个类,这些类可能位于不同的“模块”中。
默认为None,表示__module__不会被显式设置;Python 运行时将使用值sqlalchemy.ext.automap用于这些类。
当为生成的类分配__module__时,可以使用AutomapBase.by_module集合基于点分隔的模块名称进行访问。使用此钩子分配了显式__module_的类不会被放置到AutomapBase.classes集合中,只会放置到AutomapBase.by_module中。
版本 2.0 中的新内容。
另请参阅
从多个模式生成映射name_for_scalar_relationship– 用于生成标量关系的关系名称的可调用函数。默认为name_for_scalar_relationship()。name_for_collection_relationship– 用于为面向集合的关系生成关系名称的可调用函数。默认为name_for_collection_relationship()。generate_relationship– 实际生成relationship()和backref()构造的可调用函数。默认为generate_relationship()。collection_class– 当创建表示集合的新relationship()对象时将使用的 Python 集合类。默认为list。schema–
在使用AutomapBase.prepare.autoload_with参数反射表时要反射的模式名称。名称传递给MetaData.reflect.schema参数的MetaData.reflect()。当省略时,数据库连接使用的默认模式将被使用。
注意AutomapBase.prepare.schema参数支持一次反射单个模式。为了包含来自多个模式的表,请多次调用AutomapBase.prepare()。
对于多模式自动映射的概述,包括使用额外命名约定解决表名冲突,请参见 从多个模式生成映射 部分。
版本 2.0 中的新功能:AutomapBase.prepare()支持直接调用任意次数,跟踪已经处理过的表,以避免第二次处理它们。reflection_options–
当存在时,此选项字典将传递给MetaData.reflect(),以提供一般的反射特定选项,如only和/或特定于方言的选项,如oracle_resolve_synonyms。
版本 1.4 中的新功能。
function sqlalchemy.ext.automap.classname_for_table(base: Type[Any], tablename: str, table: Table) → str
返回给定表名时应该使用的类名。
默认实现是:
return str(tablename)
可以使用 AutomapBase.prepare.classname_for_table 参数指定备用实现。
参数:
base– 执行准备工作的AutomapBase类。tablename–Table的字符串名称。table–Table对象本身。
返回:
一个字符串类名。
注意
在 Python 2 中,用于类名的字符串必须是非 Unicode 对象,例如 str() 对象。Table 的 .name 属性通常是 Python 的 unicode 子类,因此应该在考虑任何非 ASCII 字符后,对此名称应用 str() 函数。
function sqlalchemy.ext.automap.name_for_scalar_relationship(base: Type[Any], local_cls: Type[Any], referred_cls: Type[Any], constraint: ForeignKeyConstraint) → str
返回应该用于从一个类到另一个类引用的属性名称,用于标量对象引用。
默认实现是:
return referred_cls.__name__.lower()
可以使用 AutomapBase.prepare.name_for_scalar_relationship 参数指定备用实现。
参数:
base– 执行准备工作的AutomapBase类。local_cls– 要映射到本地端的类。referred_cls– 要映射到引用方的类。constraint– 正在检查以产生此关系的ForeignKeyConstraint。
function sqlalchemy.ext.automap.name_for_collection_relationship(base: Type[Any], local_cls: Type[Any], referred_cls: Type[Any], constraint: ForeignKeyConstraint) → str
返回应该用于从一个类引用到另一个类的属性名称,用于集合引用。
默认实现如下:
return referred_cls.__name__.lower() + "_collection"
可以使用AutomapBase.prepare.name_for_collection_relationship参数指定备用实现。
参数:
base– 进行准备工作的AutomapBase类。local_cls– 在本地端映射的类。referred_cls– 在引用方的类。constraint– 正在检查以产生此关系的ForeignKeyConstraint。
function sqlalchemy.ext.automap.generate_relationship(base: Type[Any], direction: RelationshipDirection, return_fn: Callable[..., Relationship[Any]] | Callable[..., ORMBackrefArgument], attrname: str, local_cls: Type[Any], referred_cls: Type[Any], **kw: Any) → Relationship[Any] | ORMBackrefArgument
代表两个映射类生成relationship()或backref()。
可以使用AutomapBase.prepare.generate_relationship参数指定备用实现。
此函数的默认实现如下:
if return_fn is backref: return return_fn(attrname, **kw) elif return_fn is relationship: return return_fn(referred_cls, **kw) else: raise TypeError("Unknown relationship function: %s" % return_fn)
参数:
base– 进行准备工作的AutomapBase类。direction– 表示关系的“方向”; 这将是ONETOMANY、MANYTOONE、MANYTOMANY之一。return_fn– 默认用于创建关系的函数。这将是relationship()或backref()中的一个。backref()函数的结果将用于在第二步产生一个新的relationship(),因此如果正在使用自定义关系函数,则用户定义的实现正确区分这两个函数非常关键。attrname– 正在分配此关系的属性名称。如果generate_relationship.return_fn的值是backref()函数,则此名称是分配给反向引用的名称。local_cls– 此关系或反向引用将在本地存在的“本地”类。referred_cls– 关系或反向引用所指向的“被引用”类。**kw– 所有额外的关键字参数都将传递给函数。
返回:
一个由 generate_relationship.return_fn 参数指定的 relationship() 或 backref() 结构。
基本用法
最简单的用法是将现有数据库反映到新模型中。我们以与创建声明性基类相似的方式创建一个新的 AutomapBase 类,使用 automap_base()。然后,我们调用 AutomapBase.prepare() 在生成的基类上,要求它反映架构并生成映射:
from sqlalchemy.ext.automap import automap_base from sqlalchemy.orm import Session from sqlalchemy import create_engine Base = automap_base() # engine, suppose it has two tables 'user' and 'address' set up engine = create_engine("sqlite:///mydatabase.db") # reflect the tables Base.prepare(autoload_with=engine) # mapped classes are now created with names by default # matching that of the table name. User = Base.classes.user Address = Base.classes.address session = Session(engine) # rudimentary relationships are produced session.add(Address(email_address="foo@bar.com", user=User(name="foo"))) session.commit() # collection-based relationships are by default named # "<classname>_collection" u1 = session.query(User).first() print(u1.address_collection)
上面,在传递 AutomapBase.prepare.reflect 参数时调用 AutomapBase.prepare() 表示将在此声明基类的 MetaData 集合上调用 MetaData.reflect() 方法;然后,每个 viable Table 在 MetaData 内将自动生成一个新的映射类。将连接各个表的 ForeignKeyConstraint 对象用于在类之间生成新的双向 relationship() 对象。类和关系遵循默认命名方案,我们可以自定义。在此时,我们的基本映射由相关的 User 和 Address 类组成,可以像传统方式一样使用。
注意
这里的 viable 意味着要将表映射,必须指定主键。此外,如果检测到表是两个其他表之间的纯关联表,则不会直接映射,而是将其配置为两个引用表的映射之间的多对多表。
从现有的元数据生成映射
我们可以将预先声明的MetaData对象传递给automap_base()。这个对象可以以任何方式构建,包括以编程方式、从序列化文件中或者通过MetaData.reflect()自身进行反射。下面我们展示了反射和显式表声明的结合使用:
from sqlalchemy import create_engine, MetaData, Table, Column, ForeignKey from sqlalchemy.ext.automap import automap_base engine = create_engine("sqlite:///mydatabase.db") # produce our own MetaData object metadata = MetaData() # we can reflect it ourselves from a database, using options # such as 'only' to limit what tables we look at... metadata.reflect(engine, only=["user", "address"]) # ... or just define our own Table objects with it (or combine both) Table( "user_order", metadata, Column("id", Integer, primary_key=True), Column("user_id", ForeignKey("user.id")), ) # we can then produce a set of mappings from this MetaData. Base = automap_base(metadata=metadata) # calling prepare() just sets up mapped classes and relationships. Base.prepare() # mapped classes are ready User = Base.classes.user Address = Base.classes.address Order = Base.classes.user_order
从多个模式生成映射
当使用反射时,AutomapBase.prepare()方法最多一次只能从一个模式中反射表,使用AutomapBase.prepare.schema参数来指示要反射的模式的名称。为了将AutomapBase填充到来自多个模式的表中,可以多次调用AutomapBase.prepare(),每次传递不同的名称给AutomapBase.prepare.schema参数。AutomapBase.prepare()方法会保留一个已经映射过的Table对象的内部列表,并且只会为那些自上次运行AutomapBase.prepare()以来新的Table对象添加新的映射:
e = create_engine("postgresql://scott:tiger@localhost/test") Base.metadata.create_all(e) Base = automap_base() Base.prepare(e) Base.prepare(e, schema="test_schema") Base.prepare(e, schema="test_schema_2")
2.0 版本新增功能:AutomapBase.prepare()方法可以被任意次数调用;每次运行只会映射新添加的表。在 1.4 版本及更早版本中,多次调用会导致错误,因为它会尝试重新映射已经映射的类。直接调用MetaData.reflect()的先前解决方法仍然可用。
在多个模式中自动映射同名表
对于常见情况,即多个模式可能具有相同命名的表,因此可能生成相同命名的类,可以通过使用AutomapBase.prepare.classname_for_table挂钩来在每个模式基础上应用不同的类名来解决冲突,或者使用AutomapBase.prepare.modulename_for_table挂钩,通过更改它们的有效__module__属性来消除同名类的歧义。在下面的示例中,此挂钩用于为所有类创建一个__module__属性,其形式为mymodule.,其中如果没有模式,则使用模式名为default:
e = create_engine("postgresql://scott:tiger@localhost/test") Base.metadata.create_all(e) def module_name_for_table(cls, tablename, table): if table.schema is not None: return f"mymodule.{table.schema}" else: return f"mymodule.default" Base = automap_base() Base.prepare(e, modulename_for_table=module_name_for_table) Base.prepare(e, schema="test_schema", modulename_for_table=module_name_for_table) Base.prepare(e, schema="test_schema_2", modulename_for_table=module_name_for_table)
相同命名的类被组织成一个层次化的集合,可在AutomapBase.by_module中使用。该集合使用特定包/模块的点分隔名称进行遍历,直到所需的类名。
注意
当使用AutomapBase.prepare.modulename_for_table挂钩返回一个不是None的新__module__时,类不会放置到AutomapBase.classes集合中;只有没有给定显式模块名称的类才会放在这里,因为该集合无法表示同名类。
在上面的示例中,如果数据库中包含默认模式,test_schema模式和test_schema_2模式中的一个名为accounts的表,那么将会有三个不同的类可用:
Base.by_module.mymodule.default.accounts Base.by_module.mymodule.test_schema.accounts Base.by_module.mymodule.test_schema_2.accounts
对于所有AutomapBase类生成的默认模块命名空间是sqlalchemy.ext.automap。如果没有使用AutomapBase.prepare.modulename_for_table挂钩,则AutomapBase.by_module的内容将完全在sqlalchemy.ext.automap命名空间内(例如MyBase.by_module.sqlalchemy.ext.automap.),其中包含与AutomapBase.classes中看到的相同的一系列类。因此,通常只有在存在显式__module__约定时才需要使用AutomapBase.by_module。
在跨多个模式自动映射同名表时
对于常见情况,即多个模式可能具有相同命名的表,因此会生成相同命名的类,可以通过使用AutomapBase.prepare.classname_for_table钩子来根据每个模式应用不同的类名来解决冲突,或者通过使用AutomapBase.prepare.modulename_for_table钩子来解决相同命名类的歧义问题,该钩子允许通过更改它们的有效__module__属性来区分相同命名的类。在下面的示例中,该钩子用于创建一个形式为mymodule.的__module__属性,其中如果不存在模式,则使用模式名称default:
e = create_engine("postgresql://scott:tiger@localhost/test") Base.metadata.create_all(e) def module_name_for_table(cls, tablename, table): if table.schema is not None: return f"mymodule.{table.schema}" else: return f"mymodule.default" Base = automap_base() Base.prepare(e, modulename_for_table=module_name_for_table) Base.prepare(e, schema="test_schema", modulename_for_table=module_name_for_table) Base.prepare(e, schema="test_schema_2", modulename_for_table=module_name_for_table)
相同命名的类被组织成一个层次结构集合,可在AutomapBase.by_module中使用。该集合通过特定包/模块的点分隔名称向下遍历到所需的类名。
注意
当使用AutomapBase.prepare.modulename_for_table钩子返回一个不是None的新__module__时,该类不会被放置到AutomapBase.classes集合中;只有那些没有给定显式模块名的类会被放置在此处,因为集合不能单独表示同名类。
在上述示例中,如果数据库中包含了三个默认模式、test_schema模式和test_schema_2模式中都命名为accounts的表,则会分别获得三个单独的类:
Base.by_module.mymodule.default.accounts Base.by_module.mymodule.test_schema.accounts Base.by_module.mymodule.test_schema_2.accounts
为所有AutomapBase类生成的默认模块命名空间是sqlalchemy.ext.automap。 如果未使用AutomapBase.prepare.modulename_for_table挂钩,则AutomapBase.by_module的内容将完全在sqlalchemy.ext.automap命名空间内(例如,MyBase.by_module.sqlalchemy.ext.automap.),其中包含与AutomapBase.classes中看到的相同系列的类。 因此,仅当存在显式的__module__约定时才通常需要使用AutomapBase.by_module。
SqlAlchemy 2.0 中文文档(三十)(3)https://developer.aliyun.com/article/1562437