【问题标题】:SQL Alchemy Closure Table Relationship DefinitionSQL Alchemy 闭包表关系定义
【发布时间】:2012-01-07 17:34:41
【问题描述】:

我最近开始使用 SQL Alchemy 进行一个涉及攀登区域和路线的项目。区域是分层的,因为单个区域可能包含多个区域,而多个区域又可能包含其他区域。一条路线直接与单个区域相关联,但也与该区域的父区域相关联,等等。

为了实现这一点,我选择使用closure table ala Bill Karwin。在闭包表实现中,创建了第二个表来存储祖先/后代信息。添加节点时会创建一个自引用行,并为树中的每个祖先创建一行。

表结构如下(简化):

-- area --
area_id
name

-- area_relationship --
ancestor
descendent

-- route --
route_id
area_id
name

样本数据:

-- 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

要查询给定路线的所有区域(向上),我可以执行:

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

同样,我可以通过以下方式查询特定区域(包括后代区域)中的所有路线:

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

在 SQL Alchemy 中,我创建了一个关系和两个表来处理这些关系:

area_relationship_table = Table('area_relationship', Base.metadata,
  Column('ancestor', Integer, ForeignKey('area.area_id')),
  Column('descendent', Integer, ForeignKey('area.area_id'))
)

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)

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])

目前,我可以使用 DbRoute 中的区域关系从单个路线中确定区域。但是,当我尝试在 DbArea 中使用 backref 'routes' 时,出现以下错误:

sqlalchemy.exc.StatementError: No column route.area_id is configured on mapper|DbArea|area...(原始原因:UnmappedColumnError: No column route.area_id is configured on mapper|DbArea|area...) 'SELECT route.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 添加一些东西来建立关系,但是在尝试了一些不同的选项后无法确定解决方案。

【问题讨论】:

    标签: python sqlalchemy


    【解决方案1】:

    在 SQL Alchemy Google Group 发帖并收到一些awesome help from 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]) 
    

    关键在于正确定义连接。现在我可以轻松地从路由实例中查找祖先树中的区域,或者从区域中查找后代树中的所有路由。

    【讨论】:

      猜你喜欢
      • 2015-07-11
      • 1970-01-01
      • 2015-09-01
      • 2021-07-21
      • 1970-01-01
      • 2019-05-19
      • 1970-01-01
      • 2021-10-31
      • 2019-02-21
      相关资源
      最近更新 更多