前面一篇介绍了SQLAlchemy的入门,这里我讲讲它的进阶用法,其实主要是通过它来轻松实现一些复杂查询。
SQLAlchemy中的映射关系有四种,分别是一对多、多对一、一对一、多对多。接下来我将详细说明怎样去定义这四种关系, 然后再演示怎样通过这四种关系完成复杂的查询和更新。
一对多
表示一对多的关系时,在子表类中通过 foreign key (外键)引用父表类。
然后,在父表类中通过 relationship() 方法来引用子表的类:
1 | class Parent(Base): |
多对一
在一对多的关系中建立双向的关系,这样的话在对方看来这就是一个多对一的关系,
在子表类中附加一个relationship()
方法,并且在双方的relationship()
方法中使用relationship.back_populates
方法参数:
1 | class Parent(Base): |
这样的话子表将会在多对一的关系中获得父表的属性
或者,可以在单一的relationship()
方法中使用backref
参数来代替back_populates
参数,
推荐使用这种方式,可以少些几句话。
1 | class Parent(Base): |
一对一
一对一就是多对一和一对多的一个特例,只需在relationship加上一个参数uselist=False替换多的一端就是一对一
从一对多转换到一对一:
1 | class Parent(Base): |
从多对一转换到一对一:
1 | class Parent(Base): |
多对多
多对多关系需要一个中间关联表,通过参数secondary
来指定。backref
会自动的为子表类加载同样的secondary
参数,
所以为了简洁起见仍然推荐这种写法:
1 | from sqlalchemy import Table, Text |
如果使用back_populates
,那么两个都要定义:
1 | from sqlalchemy import Table, Text |
一些重要参数
relationship()
函数接收的参数非常多,比如:backref
,secondary
,primaryjoin
等等。
下面列举一下我用到的参数
- backref 在一对多或多对一之间建立双向关系,比如
- lazy:默认值是True, 懒加载
- remote_side: 表中的外键引用的是自身时, 如Node类,如果想表示多对一的树形关系, 那么就可以使用remote_side
1
2
3
4
5
6class Node(Base):
__tablename__ = 'node'
id = Column(Integer, primary_key=True)
parent_id = Column(Integer, ForeignKey('node.id'))
data = Column(String(50))
parent = relationship("Node", remote_side=[id]) - secondary: 多对多指定中间表关键字
- order_by: 在一对多的关系中,如下代码:
1
2
3
4
5class User(Base):
# ...
addresses = relationship(lambda: Address,
order_by=lambda: desc(Address.email),
primaryjoin=lambda: Address.user_id==User.id) - cascade: 级联删除不过不设置
1
2
3
4
5
6
7
8
9
10class Parent(Base):
__tablename__ = 'parent'
id = Column(Integer,primary_key = True)
children = relationship("Child",cascade='all',backref='parent')
def delete_parent():
session = Session()
parent = session.query(Parent).get(2)
session.delete(parent)
session.commit()cascade
,删除parent
时,其关联的chilren
不会删除,只会把chilren
关联的parent.id
置为空, 设置cascade
后就可以级联删除children
对象的四种状态
对象在session中可能存在的四种状态包括:
- Transient:实例还不在session中,还没有保存到数据库中去,没有数据库身份,想刚创建出来的对象比如User(),仅仅只有mapper()与之关联
- Pending:用add()一个transient对象后,就变成了一个pending对象,这时候仍然没有flushed到数据库中去,直到flush发生。
- Persistent:实例出现在session中而且在数据库中也有记录了,通常是通过flush一个pending实例变成Persistent或者从数据库中querying一个已经存在的实例。
- Detached:一个对象它有记录在数据库中,但是不在任何session中,
关联查询
查询:http://docs.sqlalchemy.org/en/rel_1_1/orm/query.html
关联查询:http://docs.sqlalchemy.org/en/rel_1_1/orm/query.html#sqlalchemy.orm.query.Query.join
非常简单的关联查询,外键就一个,系统知道如何去关联:
1 | session.query(User).join(Address).filter(Address.email=="lzjun@qq.com").all() |
指定ON字段:
1 | q = session.query(User).join(Address, User.id==Address.user_id) |
多个join
1 | q = session.query(User).join("orders", "items", "keywords") |
子查询JOIN:
1 | address_subq = session.query(Address).\ |
join from:
1 | q = session.query(Address).select_from(User).\ |
和下面的SQL等价:
1 | SELECT address.* FROM user |
左外连接,指定isouter=True
,等价于Query.outerjoin()
:
1 | q = session.query(Node).\ |