Basic Relationship Patterns
基本关系模式
下列的 import 语句,应用到接下来所有的代章节中:
1 from sqlalchemy import Table, Column, Integer, ForeignKey
2 from sqlalchemy.orm import relationship
3 from sqlalchemy.ext.declarative import declarative_base
4
5 Base = declarative_base()
One To Many
表示一对多的关系时,在子表类中通过 foreign key (外键)引用父表类。
然后,在父表类中通过 relationship() 方法来引用子表的类:
1 class Parent(Base):
2 __tablename__ = 'parent'
3 id = Column(Integer, primary_key=True)
4 children = relationship("Child")
5 # 在父表类中通过 relationship() 方法来引用子表的类集合
6
7 class Child(Base):
8 __tablename__ = 'child'
9 id = Column(Integer, primary_key=True)
10 parent_id = Column(Integer, ForeignKey('parent.id')) 11 # 在子表类中通过 foreign key (外键)引用父表的参考字段
在一对多得关系中建立双向的关系,这样的话在对方看来这就是一个多对一的关系,
在子表类中附加一个 relationship() 方法,并且在双方的 relationship() 方法中使用 relationship.back_populates 方法参数:
1 class Parent(Base):
2 __tablename__ = 'parent'
3 id = Column(Integer, primary_key=True)
4 children = relationship("Child", back_populates="parent")
5
6 class Child(Base):
7 __tablename__ = 'child'
8 id = Column(Integer, primary_key=True)
9 parent_id = Column(Integer, ForeignKey('parent.id')) 10 parent = relationship("Parent", back_populates="children") 11 # 子表类中附加一个 relationship() 方法 12 # 并且在(父)子表类的 relationship() 方法中使用 relationship.back_populates 参数
这样的话子表将会在多对一的关系中获得父表的属性
或者,可以在单一的 relationship() 方法中使用 backref 参数来代替 back_populates 参数:
1 class Parent(Base):
2 __tablename__ = 'parent'
3 id = Column(Integer, primary_key=True)
4 children = relationship("Child", backref="parent")
5
6 class Child(Base):
7 __tablename__ = 'child'
8 id = Column(Integer, primary_key=True)
9 parent_id = Column(Integer, ForeignKey('parent.id'))
One To One
一对一是两张表之间本质上的双向关系。
要做到这一点,只需要在一对多关系基础上的父表中使用uselist参数来表示。
把一对多转化成一对一:
1 class Parent(Base):
2 __tablename__ = 'parent'
3 id = Column(Integer, primary_key=True)
4 child = relationship("Child", uselist=False, back_populates="parent")
5
6 class Child(Base):
7 __tablename__ = 'child'
8 id = Column(Integer, primary_key=True)
9 parent_id = Column(Integer, ForeignKey('parent.id')) 10 parent = relationship("Parent", back_populates="child")
把多对一转化成一对一:
1 class Parent(Base):
2 __tablename__ = 'parent'
3 id = Column(Integer, primary_key=True)
4 child_id = Column(Integer, ForeignKey('child.id'))
5 child = relationship("Child", back_populates="parent")
6
7 class Child(Base):
8 __tablename__ = 'child'
9 id = Column(Integer, primary_key=True) 10 parent = relationship("Parent", back_populates="child", uselist=False)
As always, the relationship.backref and backref() functions may be used in lieu of the relationship.back_populates approach; to specify uselist on a backref, use the backref() function:
同样的,可以使用下面这种方式:
1 from sqlalchemy.orm import backref
2
3 class Parent(Base):
4 __tablename__ = 'parent'
5 id = Column(Integer, primary_key=True)
6 child_id = Column(Integer, ForeignKey('child.id'))
7 child = relationship("Child", backref=backref("parent", uselist=False))
8
9 class Child(Base): 10 __tablename__ = 'child' 11 id = Column(Integer, primary_key=True) 12 parent_id = Column(Integer, ForeignKey('parent.id'))
Many To Many
多对多关系会在两个类之间增加一个关联的表。
这个关联的表在 relationship() 方法中通过 secondary 参数来表示。
通常的,这个表会通过 MetaData 对象来与声明基类关联,
所以这个 ForeignKey 指令会使用链接来定位到远程的表:
多对多关系中的两个表之间的一个关联表
1 association_table = Table('association', Base.metadata,
2 Column('left_id', Integer, ForeignKey('left.id')),
3 Column('right_id', Integer, ForeignKey('right.id'))
4 )
5
6 class Parent(Base):
7 __tablename__ = 'left'
8 id = Column(Integer, primary_key=True) 9 children = relationship("Child", 10 secondary=association_table) 11 # 在父表中的 relationship() 方法传入 secondary 参数,其值为关联表的表名 12 13 class Child(Base): 14 __tablename__ = 'right' 15 id = Column(Integer, primary_key=True)
双向关系中,两个表类都会包含这个集合。
指定使用 relationship.back_populates 参数,并且为每一个 relationship() 方法指定共用的关联表:
1 association_table = Table('association', Base.metadata,
2 Column('left_id', Integer, ForeignKey('left.id')),
3 Column('right_id', Integer, ForeignKey('right.id'))
4 )
5
6 class Parent(Base):
7 __tablename__ = 'left'
8 id = Column(Integer, primary_key=True) 9 children = relationship( 10 "Child", 11 secondary=association_table, 12 back_populates="parents") 13 14 class Child(Base): 15 __tablename__ = 'right' 16 id = Column(Integer, primary_key=True) 17 parents = relationship( 18 "Parent", 19 secondary=association_table, 20 back_populates="children")
当在父表类的 relationship() 方法中使用 backref参数代替 relationship.back_populates 时,backref 会自动的为子表类加载同样的 secondary 参数。
1 association_table = Table('association', Base.metadata,
2 Column('left_id', Integer, ForeignKey('left.id')),
3 Column('right_id', Integer, ForeignKey('right.id'))
4 )
5
6 class Parent(Base):
7 __tablename__ = 'left'
8 id = Column(Integer, primary_key=True) 9 children = relationship("Child", 10 secondary=association_table, 11 backref="parents") 12 13 class Child(Base): 14 __tablename__ = 'right' 15 id = Column(Integer, primary_key=True)
secondary 参数还能够接收一个可调函数的最终返回值,
which is evaluated only when mappers are first used. Using this, we can define the association_table at a later point, as long as it’s available to the callable after all module initialization is complete:
1 class Parent(Base):
2 __tablename__ = 'left'
3 id = Column(Integer, primary_key=True)
4 children = relationship("Child",
5 secondary=lambda: association_table,
6 backref="parents")
With the declarative extension in use, the traditional “string name of the table” is accepted as well, matching the name of the table as stored in Base.metadata.tables:
1 class Parent(Base):
2 __tablename__ = 'left'
3 id = Column(Integer, primary_key=True)
4 children = relationship("Child",
5 secondary="association",
6 backref="parents")