这里说的 AdjacencyList , 就是最常用来在关系数据库中表示树结构的,parent方式:
| id | name | parent |
|---|---|---|
| 1 | 一 | null |
| 2 | 二 | 1 |
| 3 | 三 | 2 |
上面的数据, 表示的结构就是:
一
|- 二
|- 三
模型定义很好做:
1 | # -*- coding: utf-8 -*- |
这里不让parent字段有null, 而使用0代替.
这个例子在关系上, 有一个纠结的地方, 因为 node 这个表, 它是自关联的, 所以如果想要children 和 parent_obj 这两个关系时:
1 | children = relationship('Node') |
呃, 尴尬了.
如果是两个表, 那么 SQLAlchemy 可以通过外键在哪张表这个信息, 来确定关系的方向:
1 | class Blog(BaseModel): |
因为外键在 Blog 中, 所以 Blog -> User 的 user_obj 是一个 N -> 1关系.
反之, User -> Blog 的 blog_list 则是一个 1 -> N 的关系.
而自相关的 Node 无法直接判断方向, 所以 SQLAlchemy 会按 1 -> N 处理, 那么:
1 | children = relationship('Node') |
这两条之中, children 是正确的, 是我们想要的. 要定义 parent_obj 则需要在 relationship 中通过参数明确表示方向:
1 | parent_obj = relationship('Node', remote_side=[id]) |
这种方式就定义了一个, “到 id” 的 N -> 1 关系.
现在完整的模型定义是:
1 | class Node(BaseModel): |
查询方面没什么特殊的了, 不过我发现在自相关的模型关系, lazy 选项不起作用:
1 | children = relationship('Node', lazy="joined") |
都是无效的, 只有在查询时, 手动使用 options() 定义:
1 | n = session.query(Node).filter(Node.name==u'一')\ |
如果要一次查出多级的子节点:
1 | n = session.query(Node).filter(Node.name==u'一')\ |
多个 joinedload() 串连的话, 可以使用 joinedload_all() 来整合:
1 | from sqlalchemy.orm import joinedload_all |
在修改方面, 删除的话, 配置了 cascade , 删除父节点, 则子节点也会自动删除:
1 | children = relationship('Node', lazy='joined', cascade='all') # 1 -> N |
如果只删除子节点, 那么 delete-orphan 选项就很好用了:
1 | children = relationship('Node', lazy='joined', cascade='all, delete-orphan') # 1 -> N |