SQLAlchemy:在NULL父关系中查找子对象

时间:2013-11-19 14:13:31

标签: python sqlalchemy flask

我有一个有亲子关系的班级:

Base = declarative_base()

class Parent(Base):
  __tablename__ = "parent_table"
  id = Column(Integer, primary_key=True)
  children = relationship("Child", backref="parent")
  def all_children(self):
    pass # I want self.children + "Child where parent_id = NULL"

class Child(Base):
  __tablename__ = "child_table"
  id = Column(Integer, primary_key=True)
  parent_id = Column(Integer, ForeignKey('parent_table.id')

我想向父母添加一个函数,该函数返回父亲在子关系中的子项,以及所有将其parent_id列设置为NULL的子对象。

整个情况有点复杂,因为类实际上是这样的联接表继承的情况:table_per_related,但我甚至不知道从哪里开始所以可能能够弄明白从那里。

(整个事情必须在通过Flask的Web服务环境中使用)

修改:更新。这是我真正想要做的最小实现,因为我无法将第一个答案翻译成在这种情况下有效的东西:

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

from sqlalchemy.orm import Session
from sqlalchemy import create_engine

class BaseCols:
  id = Column(Integer, primary_key=True)
  name = Column(String)

  def __repr__(self):
    return "<{}: {} - {}>".format(self.__class__.__name__, self.id, self.name)

  @declared_attr
  def __tablename__(cls):
    return cls.__name__.lower()

Base = declarative_base(cls=BaseCols)

class Child(BaseCols):
  pass

class HasChild:
  @declared_attr
  def children(cls):
    cls.Child = type("{}Child".format(cls.__name__),
                     (Child, Base,),
                     dict(
                          __tablename__="{}_children".format(cls.__tablename__),
                          parent_id=Column(Integer, ForeignKey("{}.id".format(cls.__tablename__))),
                          parent=relationship(cls)
                     )
                    )
    return relationship(cls.Child)

  def all_children(self):
    pass

class Foo(Base, HasChild):
  __tablename__ = 'foo'
  __mapper_args__ = {'concrete': True}

class Bar(Base, HasChild):
  __tablename__ = 'bar'
  __mapper_args__ = {'concrete': True}


if __name__ == "__main__":
  engine = create_engine('sqlite://', echo=True)
  Base.metadata.create_all(engine)

  session = Session(engine)

  session.add_all([
    Foo(
      name = "Foo the first!",
      children = [
        Foo.Child(name="Heir Apparent."),
        Foo.Child(name="Spare.")
      ]
    ),
    Foo(
      name = "Foo the second...",
      children = [
        Foo.Child(name="Some child."),
      ]
    ),
    Bar(
      name = "Bar the first!",
      children = [
        Bar.Child(name="Bar's.")
      ]
    ),
    Foo.Child(name="whoops"),
  ])

  session.commit()

  foo1 = session.query(Foo).first()
  print(foo1)
  print(foo1.children)
  print(foo1.all_children(session))

1 个答案:

答案 0 :(得分:2)

这样的东西?

class Child(Base):
    # ...

class Parent(Base):
    # ...
    def all_children(self):
        return Child.query.filter((Child.parent_id == self.id) | (Child.parent_id == None)).all()

请注意,我已将Child课程移到Parent之上,以便可以在all_children()中引用它。

更新:以下是您添加到问题中的代码的实现:

    def all_children(self, session):
        cls = self.__class__.Child
        return session.query(cls).filter((cls.parent_id == self.id) |
            (cls.parent_id == None)).all()