我最近开始使用SQL Alchemy进行涉及攀爬区域和路线的项目.区域是分层的,因为单个区域可能包含多个区域,而这些区域又可能包含其他区域.路线直接与单个区域相关联,但也与该区域的父级相关联.
为了实现这一点,我选择使用一个封闭表ala Bill Karwin.在闭包表实现中,创建第二个表来存储祖先/后代信息.添加节点时会创建自引用行,也会为树中的每个祖先创建一行.
表结构如下(简化):
-- area --
area_id
name
-- area_relationship --
ancestor
descendent
-- route --
route_id
area_id
name
Run Code Online (Sandbox Code Playgroud)
样本数据:
-- area --
1, New River Gorge
2, Kaymoor
3, South Nuttall
4, Meadow River Gorge
-- area_relationship (ancestor, descendent) --
1, 1 (self-referencing)
2, 2 (self-referencing)
1, 2 (Kaymoor is w/i New River Gorge)
3, 3 (self-referencing)
1, 3 (South Nutall is w/i New River Gorge)
4, 4 (self-referencing)
-- route (route_id, area_id, name)
1, 2, Leave it to Jesus
2, 2, Green Piece
3, 4, Fancy Pants
Run Code Online (Sandbox Code Playgroud)
要查询给定路径的所有区域(在树上),我可以执行:
SELECT area.area_id, area.name
FROM route
INNER JOIN area_relationship ON route.area_id = area_relationship.descendent
INNER JOIN area ON area.area_id = area_relationship.ancestor
WHERE route.route_id = 1
Run Code Online (Sandbox Code Playgroud)
同样,我可以查询特定区域(包括后代区域)中的所有路线:
SELECT route.route_id, route.name
FROM area
INNER JOIN area_relationship ON area.area_id = area_relationship.ancestor
INNER JOIN route ON route.area_id = area_relationship.descendent
WHERE area.area_id = 1
Run Code Online (Sandbox Code Playgroud)
在SQL Alchemy中,我创建了一个关系和两个表来处理这些关系:
area_relationship_table = Table('area_relationship', Base.metadata,
Column('ancestor', Integer, ForeignKey('area.area_id')),
Column('descendent', Integer, ForeignKey('area.area_id'))
)
Run Code Online (Sandbox Code Playgroud)
DbArea类 -
class DbArea(Base):
__tablename__ = 'area'
area_id = Column(Integer, primary_key = True)
name = Column(VARCHAR(50))
created = Column(DATETIME)
area_relationship_table.c.ancestor])
descendents = relationship('DbArea', backref = 'ancestors',
secondary = area_relationship_table,
primaryjoin = area_id == area_relationship_table.c.ancestor,
secondaryjoin = area_id == area_relationship_table.c.descendent)
Run Code Online (Sandbox Code Playgroud)
DbRoute类 -
class DbRoute(Base):
__tablename__ = 'route'
route_id = Column(Integer, primary_key = True)
area_id = Column(Integer, ForeignKey('area.area_id'))
name = Column(VARCHAR(50))
created = Column(DATETIME)
area = relationship("DbArea")
areas = relationship('DbArea', backref = 'routes',
secondary = area_relationship_table,
primaryjoin = area_id == area_relationship_table.c.ancestor,
secondaryjoin = area_id == area_relationship_table.c.descendent,
foreign_keys=[area_relationship_table.c.ancestor,
area_relationship_table.c.descendent])
Run Code Online (Sandbox Code Playgroud)
目前,我可以使用DbRoute中的区域关系来确定单个路线的区域.但是,当我尝试在DbArea中使用backref'routes'时,我收到以下错误:
sqlalchemy.exc.StatementError:没有在mapper Mapper | DbArea | area ...上配置列route.area_id(原因:UnmappedColumnError:在mapper Mapper | DbArea | area上没有配置列route.area_id ...)'SELECT路由.route_id AS route_route_id,route.area_id AS route_area_id,route.name AS route_name,route.created AS route_created \nFROM route,area_relationship \nWHERE%s = area_relationship.descendent AND route.area_id = area_relationship.ancestor'[immutabledict({}) ]
我猜我可能需要向DbArea添加一些内容以建立关系,但在尝试了一些不同的选项后无法确定解决方案.
在发布到SQL Alchemy Google Group并获得Michael Bayer的一些非常棒的帮助之后,我得出了以下DbRoute类中区域关系的定义
areas = relationship('DbArea',
backref = backref('routes', order_by = 'DbRoute.name'),
secondary = area_relationship_table,
primaryjoin = area_id == area_relationship_table.c.descendent,
secondaryjoin = DbArea.area_id == area_relationship_table.c.ancestor,
innerjoin = True, order_by = DbArea.name,
foreign_keys =
[area_relationship_table.c.ancestor,
area_relationship_table.c.descendent])
Run Code Online (Sandbox Code Playgroud)
关键在于正确定义连接.现在,我可以轻松地从路径实例中查找祖先树中的区域,或从区域中查找区域,并查找后代树中的所有路径.
| 归档时间: |
|
| 查看次数: |
1484 次 |
| 最近记录: |