首页 > 解决方案 > SQLAlchemy:来自多个表的外键(多对多)

问题描述

我在我的烧瓶应用程序中使用了flask-sqlalchemy orm,它是关于智能家居传感器和演员的(为了简单起见,我们称它们为Nodes.

现在我想存储一个Event绑定的对象Nodes,以检查它们的状态其他或相同Nodes的状态,如果第一个状态已达到阈值,则应使用给定值设置。

此外,可以从/forGroups或中检查或设置状态Scenes。所以我有三个不同的外键要检查,另外三个要设置。每种类型都可以有多个类型,每个Event.

这是一个示例代码,其中包含db.Model我希望存储在 s 和伪代码中的内容Event

db = SQLAlchemy()

class Node(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    value = db.Column(db.String(20))
    # columns snipped out

class Group(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    value = db.Column(db.String(20))
    # columns snipped out

class Scene(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    value = db.Column(db.String(20))
    # columns snipped out


class Event(db.Model):
    id = db.Column(db.Integer, primary_key=True)

    # The following columns may be in a intermediate table
    # but I have no clue how to design that under these conditions

    constraints = # list of foreignkeys from diffrent tables (Node/Group/Scene)
                  # with threshold per key

    target = # list of foreignkeys from diffrent tables (Node/Group/Scene)
             # with target values per key

最后,我希望能够检查我的任何s 是否为 true 以相应Event地设置绑定Node// GroupScene

这可能是数据库设计问题(而不是 sqlalchemy),但我想在这里利用 sqla orm 的优势。

这个那个答案的启发,我试图深入挖掘,但关于 SO 的其他问题是关于更具体的问题或一对多关系。

非常感谢任何提示或设计技巧。谢谢!

标签: databasesqlalchemyflask-sqlalchemy

解决方案


我最终在使用和代码行之间进行了权衡。我的第一个想法是尽可能多地保存代码(DRY)并定义尽可能少的表。

正如 SQLAlchemy 本身在他们的一个示例中指出的那样,支持“通用外键”只是因为它经常被请求,而不是因为它是一个好的解决方案。使用较少的数据库功能,应用程序必须注意关键约束。

另一方面,他们说,在数据库中拥有更多表不会影响数据库性能。

所以我尝试了一些方法并找到了一个适合我用例的好方法。我没有使用用于多对多关系的“普通”中间表,而是使用另一个 SQLAlchemy 类,该类在两侧都有两个一对多关系来连接两个表。

class Event(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    noodles = db.relationship('NoodleEvent', back_populates='events')
    # columns snipped out

    def get_as_dict(self):
        return {
            "id": self.id,
            "nodes": [n.get_as_dict() for n in self.nodes]
        }


class Node(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    value = db.Column(db.String(20))
    events = db.relationship('NodeEvent', back_populates='node')
    # columns snipped out


class NodeEvent(db.Model):
    ev_id = db.Column('ev_id', db.Integer, db.ForeignKey('event.id'), primary_key=True)
    n_id = db.Column('n_id', db.Integer, db.ForeignKey('node.id'), primary_key=True)
    value = db.Column('value', db.String(200), nullable=False)
    compare = db.Column('compare', db.String(20), nullable=True)
    node = db.relationship('Node', back_populates="events")
    events = db.relationship('Event', back_populates="nodes")

    def get_as_dict(self):
        return {
            "trigger_value": self.value,
            "actual_value": self.node.status,
            "compare": self.compare
        }

权衡是每次我在该关系上绑定一个新表时,我都必须定义一个新类。但是使用“通用外键”方法,我还必须检查 ForeignKey 的来源。一天结束时做同样的工作。

通过我的get_as_dict()功能,我可以非常方便地访问相关数据。


推荐阅读