我有一个库,我通过它动态加载类。它暴露为
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来推迟加载,但是这也失败了,错误的“策略”也没有。
其他人使用了哪些方法?如果我遗漏了一些明显的东西,请告诉我。这是漫长的一天。
感谢。
答案 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
(这不是必需的,但会产生更多的对称性)。我还在那里发表评论来解释这一点,以避免有人把它放到顶部,破坏程序。