【发布时间】:2020-09-16 03:58:03
【问题描述】:
我有一个具有一对多父子关系的自我参照模型。模型实例可以链接到父实例,然后该实例将构成观察组的一部分,每个子代的observation_id 和父代是组的父代id。这个observation_id 是模型的混合属性。我想添加一些 hybrid_property 表达式来启用对这些混合属性的过滤,但我坚持使用 is_parent 表达式定义。以下是模型的摘录:
class AnnotationLabel(Model):
__tablename__ = 'annotation'
id = db.Column(db.Integer, primary_key=True)
...
parent_id = db.Column(db.ForeignKey("annotation.id", ondelete="CASCADE", nullable=True, index=True)
parent = relationship('AnnotationLabel', remote_side='AnnotationLabel.id',
backref=backref('children', passive_deletes=True, lazy='dynamic'))
@hybrid_property
def is_child(self):
"""BOOLEAN, whether or not this annotation has a linked parent annotation"""
return self.parent_id is not None
@is_child.expression
def is_child(cls):
return cls.parent_id.isnot(None)
@hybrid_property
def is_parent(self):
"""BOOLEAN, whether or not this annotation has linked children / descendants"""
return self.children.count() > 0
@is_parent.expression
def is_parent(cls):
# TODO: this does not work.
q = select([func.count(cls.id)]).where(cls.parent_id==cls.id)
print(q) # debug
return q.as_scalar() > 0
@hybrid_property
def observation_id(self):
"""INT, denoting the observation group id for linked observations of the same object (returns None if not linked)"""
return self.id if self.is_parent else self.parent_id if self.is_child else None
@observation_id.expression
def observation_id(cls):
# TODO: this may work if is_parent.expression was fixed? But haven't had a chance to test it
return db.case([(cls.is_child, cls.parent_id), (cls.is_parent, cls.id)], else_=None)
目前@is_parent.expression 似乎总是评估为假。在表达式属性中生成的 SQL(基于上例中的调试打印)看起来是这样的:
SELECT count(annotation.id) AS count_1 FROM annotation WHERE annotation.parent_id = annotation.id
这永远不会真正发生,因为一个实例通常不是它自己的父实例,而是其他实例的父实例,因此,在过滤它时,它总是什么都不返回。例如:
printfmt="ID: {a.id}, parent_id: {a.parent_id}, observation_id: {a.observation_id}, is_parent: {a.is_parent}, is_child: {a.is_child}" # instance print formatter
# THIS WORKS - returns the two child instances
for a in AnnotationLabel.query.filter(AnnotationLabel.is_child==True).all():
print(printfmt.format(a=a))
# ID: 837837, parent_id: 837838, observation_id: 837838, is_parent: False, is_child: True
# ID: 837909, parent_id: 837838, observation_id: 837838, is_parent: False, is_child: True
# THIS WORKS, PARENT INSTANCE HAS CORRECT PROPERTIES
parent = AnnotationLabel.query.get(837838) # get the parent in question
# This works, since it's using the instance attributes
print(printfmt.format(a=parent))
# ID: 837838, parent_id: None, observation_id: 837838, is_parent: True, is_child: False
# THIS DOES NOT WORK!!!??? .expression for is_parent is broken
for a in AnnotationLabel.query.filter(AnnotationLabel.is_parent==True).all():
print(printfmt.format(a=a))
# returns nothing, should be list containing 1 parent instance
# THIS ALSO DOES NOT WORK PROPERLY - ONLY RETURNS CHILDREN, NOT PARENT
for a in AnnotationLabel.query.filter(AnnotationLabel.observation_id==837838).all():
print(printfmt.format(a=a))
# ID: 837837, parent_id: 837838, observation_id: 837838, is_parent: False, is_child: True
# ID: 837909, parent_id: 837838, observation_id: 837838, is_parent: False, is_child: True
按照逻辑,我希望看到在上面的最后两个查询中返回父级 (id=837838),但事实并非如此。如果这不是一个自引用模型,我认为(?)这将适用于不同的父/子类,但在这种情况下它不起作用。
如何为类表达式 @is_parent.expression 获得与 is_parent 的实例 hybrid_property 相同的功能并使 is_parent 和 object_id 属性可查询?
任何建议将不胜感激!
【问题讨论】:
标签: python orm sqlalchemy