快速浏览基本关系模式。
以下各部分使用的导入如下:
from sqlalchemy import Table, Column, Integer, ForeignKey
from sqlalchemy.orm import relationship
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
一对多关系在引用父表的子表上放置一个外键。 relationship()
然后在父级上指定,作为引用由子级表示的项集合:
class Parent(Base):
__tablename__ = 'parent'
id = Column(Integer, primary_key=True)
children = relationship("Child")
class Child(Base):
__tablename__ = 'child'
id = Column(Integer, primary_key=True)
parent_id = Column(Integer, ForeignKey('parent.id'))
要在一对多中建立双向关系,“反向”端是多对一,请指定一个附加的 relationship()
然后用 relationship.back_populates
参数::
class Parent(Base):
__tablename__ = 'parent'
id = Column(Integer, primary_key=True)
children = relationship("Child", back_populates="parent")
class Child(Base):
__tablename__ = 'child'
id = Column(Integer, primary_key=True)
parent_id = Column(Integer, ForeignKey('parent.id'))
parent = relationship("Parent", back_populates="children")
Child
将得到一个 parent
具有多对一语义的属性。
或者, backref
选项可用于单个 relationship()
而不是使用 back_populates
::
class Parent(Base):
__tablename__ = 'parent'
id = Column(Integer, primary_key=True)
children = relationship("Child", backref="parent")
多对一将外键放置在引用子表的父表中。 relationship()
在父级上声明,将在其中创建新的标量保持属性:
class Parent(Base):
__tablename__ = 'parent'
id = Column(Integer, primary_key=True)
child_id = Column(Integer, ForeignKey('child.id'))
child = relationship("Child")
class Child(Base):
__tablename__ = 'child'
id = Column(Integer, primary_key=True)
双向行为是通过增加一秒钟来实现的。 relationship()
并应用 relationship.back_populates
双向参数:
class Parent(Base):
__tablename__ = 'parent'
id = Column(Integer, primary_key=True)
child_id = Column(Integer, ForeignKey('child.id'))
child = relationship("Child", back_populates="parents")
class Child(Base):
__tablename__ = 'child'
id = Column(Integer, primary_key=True)
parents = relationship("Parent", back_populates="child")
或者, backref
参数可以应用于单个 relationship()
,如 Parent.child
::
class Parent(Base):
__tablename__ = 'parent'
id = Column(Integer, primary_key=True)
child_id = Column(Integer, ForeignKey('child.id'))
child = relationship("Child", backref="parents")
一对一本质上是一种双向关系,两边都有一个标量属性。为了实现这一点, uselist
标志指示在关系的“多”端放置标量属性而不是集合。将一对多转换为一对一:
class Parent(Base):
__tablename__ = 'parent'
id = Column(Integer, primary_key=True)
child = relationship("Child", uselist=False, back_populates="parent")
class Child(Base):
__tablename__ = 'child'
id = Column(Integer, primary_key=True)
parent_id = Column(Integer, ForeignKey('parent.id'))
parent = relationship("Parent", back_populates="child")
或多对一:
class Parent(Base):
__tablename__ = 'parent'
id = Column(Integer, primary_key=True)
child_id = Column(Integer, ForeignKey('child.id'))
child = relationship("Child", back_populates="parent")
class Child(Base):
__tablename__ = 'child'
id = Column(Integer, primary_key=True)
parent = relationship("Parent", back_populates="child", uselist=False)
一如既往, relationship.backref
和 backref()
功能可代替 relationship.back_populates
接近;说明 uselist
在backref上,使用 backref()
功能:
from sqlalchemy.orm import backref
class Parent(Base):
__tablename__ = 'parent'
id = Column(Integer, primary_key=True)
child_id = Column(Integer, ForeignKey('child.id'))
child = relationship("Child", backref=backref("parent", uselist=False))
多对多在两个类之间添加一个关联表。关联表由 secondary
参数 relationship()
. 通常, Table
使用 MetaData
与声明性基类关联的对象,以便 ForeignKey
指令可以定位要链接的远程表:
association_table = Table('association', Base.metadata,
Column('left_id', Integer, ForeignKey('left.id')),
Column('right_id', Integer, ForeignKey('right.id'))
)
class Parent(Base):
__tablename__ = 'left'
id = Column(Integer, primary_key=True)
children = relationship("Child",
secondary=association_table)
class Child(Base):
__tablename__ = 'right'
id = Column(Integer, primary_key=True)
对于双向关系,关系的两边都包含一个集合。指定使用 relationship.back_populates
为每个 relationship()
指定公共关联表::
association_table = Table('association', Base.metadata,
Column('left_id', Integer, ForeignKey('left.id')),
Column('right_id', Integer, ForeignKey('right.id'))
)
class Parent(Base):
__tablename__ = 'left'
id = Column(Integer, primary_key=True)
children = relationship(
"Child",
secondary=association_table,
back_populates="parents")
class Child(Base):
__tablename__ = 'right'
id = Column(Integer, primary_key=True)
parents = relationship(
"Parent",
secondary=association_table,
back_populates="children")
当使用 backref
参数而不是 relationship.back_populates
,backref将自动使用相同的 secondary
反向关系的参数:
association_table = Table('association', Base.metadata,
Column('left_id', Integer, ForeignKey('left.id')),
Column('right_id', Integer, ForeignKey('right.id'))
)
class Parent(Base):
__tablename__ = 'left'
id = Column(Integer, primary_key=True)
children = relationship("Child",
secondary=association_table,
backref="parents")
class Child(Base):
__tablename__ = 'right'
id = Column(Integer, primary_key=True)
这个 secondary
的参数 relationship()
还接受一个返回最终参数的可调用文件,该参数仅在首次使用映射器时计算。使用这个,我们可以定义 association_table
稍后,只要在所有模块初始化完成后可调用文件可用,则:
class Parent(Base):
__tablename__ = 'left'
id = Column(Integer, primary_key=True)
children = relationship("Child",
secondary=lambda: association_table,
backref="parents")
使用声明性扩展时,也接受传统的“表的字符串名称”,与存储在 Base.metadata.tables
::
class Parent(Base):
__tablename__ = 'left'
id = Column(Integer, primary_key=True)
children = relationship("Child",
secondary="association",
backref="parents")
一种独特的行为 secondary
参数 relationship()
那是 Table
当对象从集合中添加或删除时,此处指定的对象将自动服从INSERT和DELETE语句。有 无需手动从此表中删除 . 从集合中删除记录的操作将具有在刷新时删除的行的效果:
# row will be deleted from the "secondary" table
# automatically
myparent.children.remove(somechild)
经常出现的一个问题是,当直接将子对象交给“辅助”表时,如何删除“辅助”表中的行 Session.delete()
::
session.delete(somechild)
这里有几种可能性:
relationship()
从 Parent
到 Child
但是有 not 一种连接特定事物的反向关系。 Child
对每个 Parent
,SQLAlchemy将不知道删除此特定项时 Child
对象,它需要维护将其链接到 Parent
. 不会删除“辅助”表。
如果有一个关系链接到一个特定的 Child
对每个 Parent
,假设它被称为 Child.parents
,默认情况下,SQLAlchemy将加载到 Child.parents
集合以定位所有 Parent
对象,并从建立此链接的“辅助”表中删除每一行。注意,这种关系不需要是双向的;sqlAlchemy严格地查看 relationship()
与 Child
正在删除的对象。
这里的一个更高性能的选项是使用数据库使用的外键来删除CASCADE指令。假设数据库支持此功能,则可以使数据库本身自动删除“辅助”表中的行,因为“子”中的引用行将被删除。可以指示SQLAlchemy放弃在 Child.parents
在本例中,使用 passive_deletes
指令 relationship()
见 使用被动删除 有关此的详细信息。
再次注意,这些行为是 only 有关的 secondary
与一起使用的选项 relationship()
. 如果处理显式映射的关联表, not 存在于 secondary
相关选项 relationship()
,可以使用级联规则来自动删除实体,以响应被删除的相关实体-请参见 级联 有关此功能的信息。
关联对象模式是多对多的变体:当关联表包含的列超出了左表和右表的外键之外的其他列时,就会使用它。而不是使用 secondary
参数,将新类直接映射到关联表。关系的左侧通过一对多引用关联对象,关联类通过多对一引用右侧。下面我们演示了一个映射到 Association
类,其中包含一个名为 extra_data
,它是一个字符串值,与 Parent
和 Child
::
class Association(Base):
__tablename__ = 'association'
left_id = Column(Integer, ForeignKey('left.id'), primary_key=True)
right_id = Column(Integer, ForeignKey('right.id'), primary_key=True)
extra_data = Column(String(50))
child = relationship("Child")
class Parent(Base):
__tablename__ = 'left'
id = Column(Integer, primary_key=True)
children = relationship("Association")
class Child(Base):
__tablename__ = 'right'
id = Column(Integer, primary_key=True)
和往常一样,双向版本使用 relationship.back_populates
或 relationship.backref
::
class Association(Base):
__tablename__ = 'association'
left_id = Column(Integer, ForeignKey('left.id'), primary_key=True)
right_id = Column(Integer, ForeignKey('right.id'), primary_key=True)
extra_data = Column(String(50))
child = relationship("Child", back_populates="parents")
parent = relationship("Parent", back_populates="children")
class Parent(Base):
__tablename__ = 'left'
id = Column(Integer, primary_key=True)
children = relationship("Association", back_populates="parent")
class Child(Base):
__tablename__ = 'right'
id = Column(Integer, primary_key=True)
parents = relationship("Association", back_populates="child")
使用其直接形式的关联模式要求子对象在附加到父对象之前与关联实例关联;同样,父对象到子对象的访问通过关联对象进行:
# create parent, append a child via association
p = Parent()
a = Association(extra_data="some data")
a.child = Child()
p.children.append(a)
# iterate through child objects via association, including association
# attributes
for assoc in p.children:
print(assoc.extra_data)
print(assoc.child)
增强关联对象模式,以便直接访问 Association
对象是可选的,SQLAlchemy提供 关联代理 延伸。此扩展允许配置属性,这些属性将通过一次访问访问两个"hop",一个"hop"访问关联的对象,另一个"hop"访问目标属性。
警告
关联对象模式 不与将关联表映射为“辅助”的单独关系协调更改 .
下面,对 Parent.children
不会与对 Parent.child_associations
或 Child.parent_associations
在python中;虽然所有这些关系都将继续正常运行,但是在 Session
过期,通常在 Session.commit()
::
class Association(Base):
__tablename__ = 'association'
left_id = Column(Integer, ForeignKey('left.id'), primary_key=True)
right_id = Column(Integer, ForeignKey('right.id'), primary_key=True)
extra_data = Column(String(50))
child = relationship("Child", backref="parent_associations")
parent = relationship("Parent", backref="child_associations")
class Parent(Base):
__tablename__ = 'left'
id = Column(Integer, primary_key=True)
children = relationship("Child", secondary="association")
class Child(Base):
__tablename__ = 'right'
id = Column(Integer, primary_key=True)
此外,正如对一个关系的更改不会自动反映在另一个关系中一样,向两个关系中写入相同的数据也会导致插入或删除语句冲突,例如下面我们在 Parent
和 Child
对象两次:
p1 = Parent()
c1 = Child()
p1.children.append(c1)
# redundant, will cause a duplicate INSERT on Association
p1.parent_associations.append(Association(child=c1))
如果你知道自己在做什么,可以使用上面这样的映射,尽管应用 viewonly=True
参数设置为“次要”关系,以避免记录冗余更改的问题。然而,为了得到一个简单的两个对象的万无一失的模式 Parent->Child
关系在仍然使用关联对象模式时,使用关联代理扩展,如中所述 关联代理 .