SQLalchemy分层混合变换

时间:2013-05-20 12:33:32

标签: python sqlalchemy hierarchical

基于http://docs.sqlalchemy.org/ru/latest/orm/extensions/hybrid.html#hybrid-transformers

Hello sqlalchemy编码员你好!

我有以下表格的数据:

from sqlalchemy import Column, Integer, ForeignKey
from sqlalchemy.orm import relationship
from sqlalchemy.ext.declarative import declarative_base

from sqlalchemy.ext.hybrid import Comparator

class GrandparentTransformer(Comparator):
    def operate(self, op, other):
        def transform(q):
            cls = self.__clause_element__()
            parent_alias = aliased(cls)
            return q.join(parent_alias, cls.parent).\
                        filter(op(parent_alias.parent, other))
        return transform

Base = declarative_base()

class Node(Base):
    __tablename__ = 'node'
    id =Column(Integer, primary_key=True)
    parent_id = Column(Integer, ForeignKey('node.id'))
    parent = relationship("Node", remote_side=id)

    @hybrid_property
    def grandparent(self):
        return self.parent.parent

    @grandparent.comparator
    def grandparent(cls):
        return GrandparentTransformer(cls)

如果我可以扩展Node类以支持前任(n)“property”,那将非常简洁。 因此,我可以将其用作:

而不是简单的祖父母
node.predecessor(0) == node
node.predecessor(1) == node.parent
node.predecessor(2) == node.parent.parent == node.grandparent
(...etc...)

session.query(Node).with_transformation(Node.grandparent.join).filter(Node.grandparent==None)

等于:

session.query(Node).with_transformation(Node.predecessor(2).join).filter(Node.predecessor(2)==None)

任何帮助表示感谢。

修改 如何在上面的结构上实现“xpath”?

如果我有一棵树:

N0(n='A')
 -N01(n='S')
 -N02(n='S')
   -N021(n='V')
   -N022(n='N')
     -N0221(n='N')
 -N03(n='Ab')

节点N03的路径= ['A','Ab']

节点N0221的路径= ['A','S','N','N']

目标: “查找其祖先在其层次结构中的任何位置都有[X,Y,...]的所有节点” 例如,查询参数(['S','N'])将返回节点:

N022

N0221

因为他们的路径如下:

N022 - 路径= A,S,N

N0221-路径= A,S,N,N

以上可以推广为不仅仅与node.name匹配。

可以按如下方式进行调整:(提案)

session.query(Node).xpath('//[@name=S]/[@name=N]/*')

或类似的东西

感谢您的帮助

1 个答案:

答案 0 :(得分:0)

from sqlalchemy import Column, Integer, ForeignKey
from sqlalchemy.orm import relationship, aliased
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import Session
from sqlalchemy.ext.hybrid import Comparator, hybrid_property, hybrid_method

class GrandparentTransformer(Comparator):
    def __init__(self, expression, levels):
        self.expression = expression
        self.levels = levels

    def operate(self, op, other):
        def transform(q):
            cls = self.__clause_element__()
            for i in xrange(self.levels):
                parent_alias = aliased(cls)
                q = q.join(parent_alias, cls.parent).\
                            filter(op(parent_alias.parent, other))
                cls = parent_alias
            return q
        return transform

Base = declarative_base()

class Node(Base):
    __tablename__ = 'node'
    id = Column(Integer, primary_key=True)
    parent_id = Column(Integer, ForeignKey('node.id'))
    parent = relationship("Node", remote_side=id)

    @hybrid_property
    def grandparent(self):
        return self.predecessor(2)

    @hybrid_method
    def predecessor(self, n):
        if n == 0:
            return self
        else:
            return self.parent.predecessor(n - 1)

    @predecessor.expression
    def predecessor(cls, n):
        return GrandparentTransformer(cls, n)

n1, n2, n3, n4 = Node(), Node(), Node(), Node()
n1.parent = n2
n2.parent = n3
n3.parent = n4

assert n1.predecessor(2) is n3 is n1.grandparent
assert n1.predecessor(1) is n2
assert n1.predecessor(0) is n1

session = Session()

print session.query(Node).with_transformation(Node.grandparent == Node(id=5))

print session.query(Node).with_transformation(Node.predecessor(2) == Node(id=5))

print session.query(Node).with_transformation(Node.predecessor(5) == Node(id=5))