自动程序
定义对 sqlalchemy.ext.declarative
从数据库模式自动生成映射类和关系的系统,通常不一定是反映出来的。
0.9.1 新版功能: 补充 sqlalchemy.ext.automap
.
希望 AutomapBase
系统为解决著名的 SQLSoup 还试图解决从现有数据库中快速生成基本对象模型的问题。通过严格地在映射器配置级别解决这个问题,并与现有的声明性类技术完全集成, AutomapBase
寻求提供一种很好的集成方法来方便地自动生成即席映射。
基本用途
最简单的用法是将现有数据库反映到新模型中。我们创造了一个新的 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(engine, reflect=True) # 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" print (u1.address_collection)
以上呼叫 AutomapBase.prepare()
当经过 AutomapBase.prepare.reflect
参数表示 MetaData.reflect()
将对此声明性基类调用方法' MetaData
收集;然后,每个 可行的 Table
内 MetaData
将自动生成新的映射类。这个 ForeignKeyConstraint
将各种表链接在一起的对象将用于生成新的双向 relationship()
类之间的对象。类和关系遵循我们可以自定义的默认命名方案。在这一点上,我们的基本映射包括 User
和 Address
类可以以传统的方式使用。
注解
通过 可行的 ,我们的意思是,对于要映射的表,它必须指定主键。此外,如果检测到该表是其他两个表之间的纯关联表,则不会直接映射该表,而是将其配置为两个引用表的映射之间的多对多表。
从现有元数据生成映射
我们可以通过预申报 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, Address, Order = Base.classes.user, Base.classes.address,\ Base.classes.user_order
显式指定类
这个 automap
扩展允许以类似于 DeferredReflection
班级。扩展自 AutomapBase
类似于常规的声明性类,但在构造后不会立即映射,而是在调用时映射 AutomapBase.prepare()
. 这个 AutomapBase.prepare()
方法将使用我们根据使用的表名建立的类。如果我们的模式包含表 user
和 address
,我们可以定义一个或两个要使用的类:
from sqlalchemy.ext.automap import automap_base from sqlalchemy import create_engine # automap base Base = automap_base() # pre-declare User for the 'user' table class User(Base): __tablename__ = 'user' # override schema elements like Columns user_name = Column('name', String) # override relationships too, if desired. # we must use the same name that automap would use for the # relationship, and also must refer to the class name that automap will # generate for "address" address_collection = relationship("address", collection_class=set) # reflect engine = create_engine("sqlite:///mydatabase.db") Base.prepare(engine, reflect=True) # we still have Address generated from the tablename "address", # but User is the same as Base.classes.User now Address = Base.classes.address u1 = session.query(User).first() print (u1.address_collection) # the backref is still there: a1 = session.query(Address).first() print (a1.user)
上面,其中一个更复杂的细节是我们说明了 relationship()
automap将创建的对象。要做到这一点,我们需要确保名称与automap通常生成的名称相匹配,因为关系名称是 User.address_collection
从automap的角度来看,所引用的类的名称称为 address
即使我们把它称为 Address
在这个类的用法中。
覆盖命名方案
automap
的任务是基于架构生成映射的类和关系名称,这意味着它在如何确定这些名称方面有决策点。这三个决策点是使用可以传递给 AutomapBase.prepare()
方法,并称为 classname_for_table()
, name_for_scalar_relationship()
,以及 name_for_collection_relationship()
。下面的示例提供了这些函数中的任何一个或所有函数,其中我们对类名使用“驼峰大小写”方案,对集合名称使用 Inflect 套餐::
import re import inflect def camelize_classname(base, tablename, table): "Produce a 'camelized' class name, e.g. " "'words_and_underscores' -> 'WordsAndUnderscores'" return str(tablename[0].upper() + \ re.sub(r'_([a-z])', lambda m: m.group(1).upper(), tablename[1:])) _pluralizer = inflect.engine() def pluralize_collection(base, local_cls, referred_cls, constraint): "Produce an 'uncamelized', 'pluralized' class name, e.g. " "'SomeTerm' -> 'some_terms'" referred_name = referred_cls.__name__ uncamelized = re.sub(r'[A-Z]', lambda m: "_%s" % m.group(0).lower(), referred_name)[1:] pluralized = _pluralizer.plural(uncamelized) return pluralized from sqlalchemy.ext.automap import automap_base Base = automap_base() engine = create_engine("sqlite:///mydatabase.db") Base.prepare(engine, reflect=True, classname_for_table=camelize_classname, name_for_collection_relationship=pluralize_collection )
根据上面的映射,我们现在将有类 User
和 Address
,其中的集合来自 User
到 Address
被称为 User.addresses
::
User, Address = Base.classes.User, Base.classes.Address u1 = User(addresses=[Address(email="foo@bar.com")])
关系检测
automap所完成的绝大多数工作是 relationship()
基于外键的结构。这对多对一和一对多关系起作用的机制如下:
给定的
Table
已知映射到特定类的ForeignKeyConstraint
物体。从每个
ForeignKeyConstraint
远方Table
存在的对象与要将其映射到的类(如果有)匹配,否则将跳过该对象。作为
ForeignKeyConstraint
我们正在检查与即时映射类的引用相对应的关系,该关系将设置为引用引用该类的多对一关系;将在引用该类的引用类上创建一个对应的一对多backref。如果任何列是
ForeignKeyConstraint
不能为空(例如nullable=False
a)relationship.cascade
的关键字参数all, delete-orphan
将添加到要传递给关系或backref的关键字参数中。如果ForeignKeyConstraint
报道说ForeignKeyConstraint.ondelete
设置为CASCADE
对于非空值或SET NULL
对于可以为空的列集,选项relationship.passive_deletes
标志设置为True
在relationship关键字参数集中。请注意,并非所有后端都支持删除时的反射。1.0.0 新版功能: -automap将在生成一对多关系时检测不可为空的外键约束,并建立默认的级联
all, delete-orphan
如果是;另外,如果约束指定ForeignKeyConstraint.ondelete
属于CASCADE
对于不可为空的或SET NULL
对于可以为空的列,passive_deletes=True
还添加了选项。关系的名称使用
AutomapBase.prepare.name_for_scalar_relationship
和AutomapBase.prepare.name_for_collection_relationship
可调用函数。需要注意的是,默认关系命名从 实际类名 . 如果您通过声明一个特定的类给了它一个显式的名称,或者指定了一个备用的类命名方案,那么这就是从中派生关系名称的名称。将检查类是否存在与这些名称匹配的已映射属性。如果在一侧检测到一个,但在另一侧未检测到一个,
AutomapBase
尝试在缺少的一侧创建关系,然后使用relationship.back_populates
参数,以便将新关系指向另一侧。通常情况下,双方都没有关系,
AutomapBase.prepare()
产生一个relationship()
在“多对一”的一侧,并使用relationship.backref
参数。生产
relationship()
以及可选的backref()
被移交给AutomapBase.prepare.generate_relationship
函数,该函数可由最终用户提供,以增加传递给relationship()
或backref()
或者使用这些函数的自定义实现。
自定义关系参数
这个 AutomapBase.prepare.generate_relationship
hook可用于向关系添加参数。在大多数情况下,我们可以利用现有的 generate_relationship()
函数返回对象,在用我们自己的参数扩充给定的关键字字典之后。
下面是如何发送的示例 relationship.cascade
和 relationship.passive_deletes
所有一对多关系的选项:
from sqlalchemy.ext.automap import generate_relationship def _gen_relationship(base, direction, return_fn, attrname, local_cls, referred_cls, **kw): if direction is interfaces.ONETOMANY: kw['cascade'] = 'all, delete-orphan' kw['passive_deletes'] = True # make use of the built-in function to actually return # the result. return generate_relationship(base, direction, return_fn, attrname, local_cls, referred_cls, **kw) from sqlalchemy.ext.automap import automap_base from sqlalchemy import create_engine # automap base Base = automap_base() engine = create_engine("sqlite:///mydatabase.db") Base.prepare(engine, reflect=True, generate_relationship=_gen_relationship)
多对多关系
automap
将生成多对多关系,例如那些包含 secondary
争论。生产这些产品的过程如下:
给定的
Table
检查ForeignKeyConstraint
对象,在将任何映射类分配给它之前。如果表中有两个,正好是两个
ForeignKeyConstraint
对象和此表中的所有列都是这两个对象的成员ForeignKeyConstraint
对象,该表被假定为“辅助”表,并且将 不直接映射 .两个(或一个,用于自引用)外部表,
Table
引用与将要映射到的类(如果有)匹配。如果为两侧定位了映射类,则为多对多双向
relationship()
/backref()
在两个类之间创建对。多对多的覆盖逻辑与一对多/多对一的覆盖逻辑工作相同;
generate_relationship()
调用函数生成结构,并维护现有属性。
与继承的关系
automap
不会在继承关系中的两个类之间生成任何关系。也就是说,有以下两个类:
class Employee(Base): __tablename__ = 'employee' id = Column(Integer, primary_key=True) type = Column(String(50)) __mapper_args__ = { 'polymorphic_identity':'employee', 'polymorphic_on': type } class Engineer(Employee): __tablename__ = 'engineer' id = Column(Integer, ForeignKey('employee.id'), primary_key=True) __mapper_args__ = { 'polymorphic_identity':'engineer', }
外键来自 Engineer
到 Employee
不是用于关系,而是用于在两个类之间建立联接继承。
注意,这意味着automap不会生成 any 从子类链接到超类的外键的关系。如果映射具有从子类到超类的实际关系,那么这些关系也需要是显式的。下面,因为我们有两个独立的 Engineer
到 Employee
我们需要建立我们想要的关系以及 inherit_condition
,因为这些不是SQLAlchemy可以猜测的:
class Employee(Base): __tablename__ = 'employee' id = Column(Integer, primary_key=True) type = Column(String(50)) __mapper_args__ = { 'polymorphic_identity':'employee', 'polymorphic_on':type } class Engineer(Employee): __tablename__ = 'engineer' id = Column(Integer, ForeignKey('employee.id'), primary_key=True) favorite_employee_id = Column(Integer, ForeignKey('employee.id')) favorite_employee = relationship(Employee, foreign_keys=favorite_employee_id) __mapper_args__ = { 'polymorphic_identity':'engineer', 'inherit_condition': id == Employee.id }
处理简单命名冲突
如果在映射过程中发生命名冲突,请重写 classname_for_table()
, name_for_scalar_relationship()
和 name_for_collection_relationship()
根据需要。例如,如果automap试图将多对一关系命名为与现有列相同的关系,则可以有条件地选择替代约定。给定模式:
CREATE TABLE table_a ( id INTEGER PRIMARY KEY ); CREATE TABLE table_b ( id INTEGER PRIMARY KEY, table_a INTEGER, FOREIGN KEY(table_a) REFERENCES table_a(id) );
以上模式将首先自动映射 table_a
表作为名为 table_a
;然后它将自动将关系映射到类 table_b
与此相关类同名,例如 table_a
. 此关系名称与映射列冲突 table_b.table_a
,并在映射时发出错误。
我们可以使用以下下划线来解决此冲突:
def name_for_scalar_relationship(base, local_cls, referred_cls, constraint): name = referred_cls.__name__.lower() local_table = local_cls.__table__ if name in local_table.columns: newname = name + "_" warnings.warn( "Already detected name %s present. using %s" % (name, newname)) return newname return name Base.prepare(engine, reflect=True, name_for_scalar_relationship=name_for_scalar_relationship)
或者,我们可以更改列侧的名称。可以使用中描述的技术修改映射的列。 从属性名称清楚地命名列 ,方法是将列显式分配给新名称::
Base = automap_base() class TableB(Base): __tablename__ = 'table_b' _table_a = Column('table_a', ForeignKey('table_a.id')) Base.prepare(engine, reflect=True)
在显式声明中使用automap
如前所述,automap不依赖于反射,并且可以使用 Table
中的对象 MetaData
收集。因此,可以使用automap生成缺少的关系,因为给定了一个完全定义表元数据的完整模型:
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
映射 ForeignKey
我们定义的 Address.user_id
允许双向关系对 Address.user
和 User.address_collection
在映射类上生成。
注意当子类化时 AutomapBase
, the AutomapBase.prepare()
方法是必需的;如果不调用,则声明的类处于未映射状态。
截取列定义
这个 MetaData
和 Table
对象支持事件钩子 DDLEvents.column_reflect()
它可以用来截获在 Column
对象被构造。例如,如果我们想使用命名约定映射列,例如 "attr_<columnname>"
,事件可以应用为:
@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(engine, reflect=True)
1.4.0b2 新版功能: 这个 DDLEvents.column_reflect()
事件可以应用于 MetaData
对象。
参见
从反射表自动化列命名方案 -在ORM映射文档中
API引用
Object Name | Description |
---|---|
automap_base([declarative_base], **kw) | 生成声明性automap基。 |
“automap”架构的基类。 | |
classname_for_table(base, tablename, table) | 返回应使用的类名(给定表名)。 |
generate_relationship(base, direction, return_fn, attrname, ..., **kw) | 生成一个 |
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=None, **kw)¶
生成声明性automap基。
此函数生成一个新的基类,它是
AutomapBase
类以及由生成的声明性基declarative_base()
.除
declarative_base
是直接传递给declarative_base()
功能。
- class sqlalchemy.ext.automap.AutomapBase¶
“automap”架构的基类。
这个
AutomapBase
类可以与由declarative_base()
功能。在实践中,AutomapBase
类总是与实际的声明性基一起用作mixin。一个新的子类
AutomapBase
通常使用automap_base()
功能。参见
- attribute
sqlalchemy.ext.automap.AutomapBase.
classes = None¶ 的实例
Properties
包含类。此对象的行为与
.c
表上的集合。类以它们的名称出现,例如:Base = automap_base() Base.prepare(engine=some_engine, reflect=True) User, Address = Base.classes.User, Base.classes.Address
- method
sqlalchemy.ext.automap.AutomapBase.
classmethod prepare(autoload_with=None, engine=None, reflect=False, schema=None, classname_for_table=None, collection_class=None, name_for_scalar_relationship=None, name_for_collection_relationship=None, generate_relationship=None, reflection_options={})¶ 从中提取映射类和关系
MetaData
并执行映射。- 参数
engine¶ -- 一个
Engine
或Connection
如果指定,则使用它执行模式反射。如果AutomapBase.prepare.reflect
参数为False,未使用此对象。。已弃用::1.4AutomapBase.prepare.engine
参数已弃用,将在将来的版本中删除。请使用AutomapBase.prepare.autoload_with
参数。reflect¶ -- 如果是真的,
MetaData.reflect()
方法在上调用MetaData
与此关联AutomapBase
. 这个Engine
通过AutomapBase.prepare.engine
将用于执行反射(如果存在);否则,MetaData
应该已经绑定到某个引擎,否则操作将失败。。已弃用::1.4AutomapBase.prepare.reflect
参数已弃用,将在将来的版本中删除。反射在以下情况下启用AutomapBase.prepare.autoload_with
通过。classname_for_table¶ -- 在给定表名的情况下,用于生成新类名的可调用函数。默认为
classname_for_table()
.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()
创建了表示集合的对象。默认为list
.schema¶ -- 当与
AutomapBase.prepare.reflect
标志,传递给MetaData.reflect()
指示表应该从中反映的主模式。省略时,将使用数据库连接使用的默认架构。…添加的版本:1.1reflection_options¶ -- 出现时,此选项字典将传递到
MetaData.reflect()
提供特定于反射的常规选项,如only
和/或特定于方言的选项,如oracle_resolve_synonyms
。。。添加的版本::1.4
- attribute
- function sqlalchemy.ext.automap.classname_for_table(base, tablename, table)¶
返回应使用的类名(给定表名)。
默认实现为:
return str(tablename)
- function sqlalchemy.ext.automap.name_for_scalar_relationship(base, local_cls, referred_cls, constraint)¶
对于标量对象引用,返回应该用于从一个类引用到另一个类的属性名。
默认实现为:
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, local_cls, referred_cls, constraint)¶
对于集合引用,返回应用于从一个类引用到另一个类的属性名。
默认实现为:
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, direction, return_fn, attrname, local_cls, referred_cls, **kw)¶
生成一个
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()
函数,则此名称是分配给backref的名称。local_cls¶ -- 此关系或backref将在本地存在的“本地”类。
referred_cls¶ -- 关系或backref所指的“引用的”类。
**kw¶ -- 所有其他关键字参数都将传递给函数。
- 返回
一
relationship()
或backref()
按照generate_relationship.return_fn
参数。