【发布时间】:2020-01-14 13:54:49
【问题描述】:
我有一个带有 @reconstructor 方法的映射类,该方法根据与此类的关系创建查找字典。 这行得通。 它在 init 中(显式)调用,然后在从数据库重建时也由 sqlalchemy 调用,并将每个实例的相关“化合物”映射到 self.compound,例如 self.compound[' Compound_name'] 从相关字段 self.compounds 中检索具有该名称的化合物。它在恒定时间内起作用,与我之前对 self.compounds 的线性搜索相反。太好了……
@reconstructor
def _create_lookup(self):
print('Create lookup called.')
attr = getattr(self, 'compounds')
lookup = dict(zip(
[getattr(c, 'name') for c in attr],
[getattr(c, '', c) for c in attr]
))
setattr(self, 'compound', lookup)
然而,下面更通用的形式在用作类装饰器时不起作用(我想要这个是因为我的几个类都需要这种行为,但不一定能很好地继承):
def give_class_lookup_on_attr(attr_to_lookup, lookup_key_attr, lookup_value_attr, lookup_name):
# None is a key for the lookup_value_attr to return the object itself (set in the default)
if lookup_value_attr is None:
lookup_value_attr = ''
# passing an empty string will never find an attribute, which makes it the key for returning the default
print('give_class_... decorator called.')
def class_wrap(cls):
@reconstructor
def func(self):
print('Create lookup called.')
attr = getattr(self, attr_to_lookup)
lookup = dict(zip(
[getattr(c, lookup_key_attr) for c in attr],
[getattr(c, lookup_value_attr, c) for c in attr]
# return the object as the value in dict if attr is not present
))
setattr(self, lookup_name, lookup)
cls._create_lookup = func
print('reconstructor method added to cls and cls returned')
return cls
return class_wrap
根据打印语句,我已经能够确认,当我使用适当的参数在我的类上调用我的装饰器时,重构器是在类上创建的,但从未被调用。我还检查了类方法的 .__sa_reconstructor__ 标志,该标志设置为 True(重构器装饰器对方法所做的唯一事情)。但是,它仍然没有被调用。
因为类上的装饰器是在创建类之后调用的,然后传入类,所以我的工作假设是 sqlalchemy 的映射器已经完成了它的工作(这就是为什么当内联到类,但不使用类装饰器),因此它永远不会找到添加装饰器的重构器方法。
那么,我的假设是否正确,是否有任何方法可以刷新 sqlalchemy 的映射器以使其在装修后重新审视?或者,我应该开始考虑编写和/或添加我自己的 sqlalchemy 事件侦听器(假设它们不会有同样的问题)。
编辑: 我的假设似乎进一步证实了:
mapper = class_mapper(MyMappedClass)
print(mapper._reconstructor)
如果我在测试中使用内联方法声明调用上述内容,它会返回重构器(函数 MyMappedClass._create_lookup 位于 0x7f452a49e8c8),但在使用我的类装饰器时返回 None。
【问题讨论】:
-
装饰器只是对事件系统的方便:docs.sqlalchemy.org/en/13/orm/…
-
感谢@IljaEverilä 我希望避免这种情况,但实际上对于 load 事件实现起来非常简单。我现在有点坚持让我正在添加的方法(cls._create_lookup)被调用在调用_init_之后,'init'事件显式根据文档,不这样做。这有什么共同的模式吗?
标签: python python-3.x orm sqlalchemy