SQLAlchemy邻接列表查询

时间:2018-02-15 22:07:39

标签: python flask sqlalchemy

我尝试创建一个简单的Flask应用程序,该应用程序将网络设备及其成员资格建模为任意命名的域(如果相关,则该工具将用于定义设备之间的MPLS LSP网格。我使用sqlite开发和生产将是postgres)。关系应如下:

  • 域到子域:一对多
  • 域到设备:一对多
  • 子域到域:一对一
  • 设备到域:一对一

这是我的模特:

class Device(db.Model):
    __tablename__ = 'device'
    id = db.Column(db.Integer, primary_key=True)
    hostname = db.Column(db.String(255), unique=True)
    mgmt_ip = db.Column(db.String(255), unique=True)
    snmp_comm = db.Column(db.String(255))
    domain_id = db.Column(db.Integer, db.ForeignKey('domain.id'))

    def __repr__(self):
        return '<Hostname %r>' % (self.hostname)

class Domain(db.Model):
    __tablename__ = 'domain'
    id = db.Column(db.Integer, primary_key=True)
    parent_id = db.Column(db.Integer, db.ForeignKey('domain.id'))
    name = db.Column(db.String(255), unique=True)
    children = db.relationship("Domain")
    devices = db.relationship("Device")

    def __repr__(self):
        return '<Domain %r>' % (self.name)

如何构建我的SQLAlchemy查询以启动设备本身并递归 up 树以获取给定的根域(没有父项)以生成每个设备的列表域上树?举个例子:

from app import db
from app.models import Device, Domain

db.create_all()

d1 = Domain(name='mandatory')
db.session.add(d1)
db.session.commit()
d2 = Domain(name='metro_A', parent_id=1)
db.session.add(d2)
db.session.commit()
d3 = Domain(name='metro_B', parent_id=1)
db.session.add(d3)
db.session.commit()

dev1 = Device(hostname='switch_1', mgmt_ip='1.1.1.1', snmp_comm='public', domain_id=1)
dev2 = Device(hostname='switch_2', mgmt_ip='2.2.2.2', snmp_comm='public', domain_id=1)
dev3 = Device(hostname='switch_3', mgmt_ip='3.3.3.3', snmp_comm='public', domain_id=2)
dev4 = Device(hostname='switch_4', mgmt_ip='4.4.4.4', snmp_comm='public', domain_id=2)
dev5 = Device(hostname='switch_5', mgmt_ip='5.5.5.5', snmp_comm='public', domain_id=3)
dev6 = Device(hostname='switch_6', mgmt_ip='6.6.6.6', snmp_comm='public', domain_id=3)

db.session.add_all([dev1, dev2, dev3, dev4, dev5, dev6])

db.session.commit()

此处的目标是,在switch_1作为输入的情况下,如何获取其域中的其他设备列表, plus 其父域中的设备(如果是适用于现实世界,递归,直到我到达其根域)?

1 个答案:

答案 0 :(得分:-1)

可以使用SQL中的递归公用表表达式来遍历树结构。鉴于您的目标是获取设备的域及其可能的父域,然后是这些域中的所有设备,您可以首先创建CTE来获取域:

domain_alias = db.aliased(Domain)

# Domain of switch_1 has no parents, so for demonstration switch_6
# is a better target.
initial = db.session.query(Domain.id, Domain.parent_id).\
    join(Device).\
    filter_by(hostname='switch_6').\
    cte(recursive=True)

child = db.aliased(initial)

domain_query = initial.union(
    db.session.query(domain_alias.id, domain_alias.parent_id).
        join(child, child.c.parent_id == domain_alias.id))

然后只需获取找到的域中的设备:

db.session.query(Device).\
    join(domain_query, domain_query.c.id == Device.domain_id).\
    all()