发现SQLAlchemy对象的引用

时间:2010-02-17 10:38:00

标签: python orm sqlalchemy

我有很多模型类,它们之间有关系,需要编辑CRUD接口。问题是某些对象无法删除,因为有其他对象引用它们。有时我可以设置ON DELETE规则来处理这种情况,但在大多数情况下,我不希望自动删除相关对象,直到它们被手动解除绑定。无论如何,我想向编辑器呈现一个引用当前查看对象的对象列表,并突出显示那些因FOREIGN KEY约束而无法删除的对象。是否有自动发现引用的现成解决方案?

更新

任务似乎很常见(例如django ORM显示所有依赖项),所以我想知道它还没有解决方案。

有两个方向建议:

  1. 枚举当前对象的所有关系并通过他们的backref。但无法保证所有关系都已定义backref。此外,在某些情况下backref毫无意义。虽然我可以在任何地方定义它,但我不喜欢这样做而且它不可靠。
  2. (由van和stephan建议)检查MetaData对象的所有表并从foreign_keys属性中收集依赖项(sqlalchemy_schemadisplay的代码可以作为示例使用,感谢stephan的评论)。这将允许捕获之间的所有依赖关系,但我需要的是模型类之间的依赖关系。一些外键在中间表中定义,并且没有与它们对应的模型(在关系中用作secondary)。当然,我可以走得更远并找到相关的模型(必须找到一种方法来做到这一点),但它看起来太复杂了。
  3. 解决方案

    下面是我用作解决方案的基本模型类(为声明性扩展而设计)的方法。它并不完美,不符合我的所有要求,但它适用于我项目的当前状态。结果被收集为字典字典,因此我可以通过对象及其属性显示它们。我还没有决定这是否是个好主意,因为引用者名单有时是巨大的,我被迫将其限制在一些合理的数字。

    def _get_referers(self):
        db = object_session(self)
        cls, ident = identity_key(instance=self)
        medatada = cls.__table__.metadata
        result = {}
        # _mapped_models is my extension. It is collected by metaclass, so I didn't
        # look for other ways to find all model classes.
        for other_class in medatada._mapped_models:
            queries = {}
            for prop in class_mapper(other_class).iterate_properties:
                if not (isinstance(prop, PropertyLoader) and \
                        issubclass(cls, prop.mapper.class_)):
                    continue
                query = db.query(prop.parent)
                comp = prop.comparator
                if prop.uselist:
                    query = query.filter(comp.contains(self))
                else:
                    query = query.filter(comp==self)
                count = query.count()
                if count:
                    queries[prop] = (count, query)
            if queries:
                result[other_class] = queries
        return result
    

    感谢所有帮助我的人,尤其是斯蒂芬和范。

3 个答案:

答案 0 :(得分:6)

SQL:我绝对不同意S.Lott'answer。 我不知道开箱即用的解决方案,但是绝对可能来发现对给定表具有ForeignKey约束的所有表。一个人需要正确使用INFORMATION_SCHEMA视图,例如REFERENTIAL_CONSTRAINTSKEY_COLUMN_USAGETABLE_CONSTRAINTS等。请参阅SQL Server example。通过一些限制和扩展,大多数新版本的关系数据库都支持INFORMATION_SCHEMA标准。当您拥有表中的所有FK信息和对象(行)时,只需运行少量SELECT语句即可获取其他表中引用给定行的所有其他行并防止其被删除。

SqlAlchemy:正如 stephan 在评论中所指出的那样,如果您使用ormbackref进行关系,则应该非常容易为了获得对象的列表,这些对象保留对您要删除的对象的引用,因为这些对象基本上是对象的映射属性(child1.Parent)。

如果你使用sql炼金术的Table个对象(或者不总是使用backref来表示关系),那么你必须为所有表获得foreign_keys的值,然后对于所有这些ForeignKey的调用references(...)方法,将您的表作为参数提供。通过这种方式,您将找到所有引用您的对象映射到的表的FK(和表)。然后,您可以通过构造每个FK的查询来查询所有引用您的对象的对象。

答案 1 :(得分:1)

通常,无法在关系数据库中“发现”所有引用。

在某些数据库中,它们可能以显式外键或检查约束的形式使用声明性引用完整性。

但没有要求这样做。它可能不完整或不一致。

任何查询都可以包含未声明的FK关系。如果没有所有查询的范围,您就无法知道所使用但未声明的关系。

要查找“referers”,您必须真正了解数据库设计并拥有所有查询。

答案 2 :(得分:0)

对于每个模型类,只需在每种情况下询问列表并查看它包含的条目数,就可以轻松查看其所有一对多关系是否为空。 (对于COUNT,可能还有一种更有效的方法。)如果有任何与该对象相关的外键,并且您正确设置了对象关系,那么这些列表中至少有一个将是非零的长度。