2

我有一个库,通过它动态加载类。它被曝光为

mylib.registry.<className>

registry 是一个类的实例,它包含一个类名(字符串)到模块名的字典,以及一个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 来延迟加载,但这也因缺少“策略”的错误而失败。

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

谢谢。

4

1 回答 1

1

你永远不应该在彼此不了解的两个方面使用关系。通常,通过使用可以避免这种情况,backref但在您的情况下,这会产生问题,因为您希望每一方都自己了解其关系。这里的诀窍是back_populates关键字,由提供relationship

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(这不是必需的,但会产生更多的对称性)。我还会在那里发表评论来解释这一点,以避免有人把它放在首位,破坏程序。

于 2013-09-05T12:37:22.590 回答