使用sqlalchemy加载动态关系类

时间:2013-09-04 19:31:51

标签: python sqlalchemy

我有一个库,我通过它动态加载类。它暴露为

mylib.registry.<className>

注册表是一个类的实例,它包含类名(字符串)到模块名称的字典,以及一个 getattr 调用,如果请求它,它会动态加载一个类。因此,用户可以在不必处理模块位置的情况下引用任何类(类名称的全局命名空间,但不是模块名称)。

例如,条目:

{'X', 'mylib.sublib.x',
 'Y', 'mylib.sublib.y'}

然后可以像:

一样使用
import mylib

x = mylib.registry.X()
y = mylib.registry.Y()

这就是背景。除此之外,这些对象是sqlalchemy ORM类,它们彼此之间具有关系。我们假设X与Y有一对多。

假设这个定义。

class X(Base):
    y_id = Column(Integer, ForeignKey('y.id'))
    y = relationship('Y')

class Y(Base):
    xs = relationship('X')

这些文件位于不同的文件中,每个文件都会导入顶级注册表。

所以这就是问题 - 如何在不预先加载每个课程的情况下解决这个问题?

上面的示例不起作用,因为如果我通过注册表仅导入X,则Y不在sqlalchemy类注册表中,因此相关性会中断。

如果我导入注册表本身然后直接引用这些类,那么由于相互依赖性,模块不会加载。

我尝试使用lambda来推迟加载,但是这也失败了,错误的“策略”也没有。

其他人使用了哪些方法?如果我遗漏了一些明显的东西,请告诉我。这是漫长的一天。

感谢。

1 个答案:

答案 0 :(得分:1)

你绝不应该使用彼此不了解的双方关系。通常,使用backref可以避免这种情况,但在您的情况下,这会产生问题,因为您希望每一方都能够自己意识到它的关系。这里的诀窍是由relationship提供的back_populates关键字:

y = relationship("Y", back_populates="xs")

xs = relationship("X", back_populates="y")

应用这些将使他们彼此了解。但是,它不会解决您的导入问题。通常情况下,您现在只需from x import X侧的Y。但另一种方法是行不通的,因为它会创建一个循环导入。

诀窍很简单:在要导入的类之后放置导入。由于relationship中的字符串是懒惰评估的,因此您可以在定义关系后导入类。因此X执行此操作:

class X(Base):
    __tablename__ = 'x'
    id = Column(Integer, primary_key=True)
    y_id = Column(ForeignKey('y.id'))
    y = relationship("Y", back_populates="xs")

from y import Y

另一种方式是Y(这不是必需的,但会产生更多的对称性)。我还在那里发表评论来解释这一点,以避免有人把它放到顶部,破坏程序。