【问题标题】:SQLAlchemy: find child objects in a relationship with NULL parentSQLAlchemy:查找与 NULL 父对象有关系的子对象
【发布时间】:2013-11-20 23:10:19
【问题描述】:

我有一个具有父子关系的类:

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))

【问题讨论】:

    标签: python sqlalchemy flask


    【解决方案1】:

    这样的?

    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()
    

    【讨论】:

    • 是的!我还有一些细节要弄清楚 table_per_related 设置,但如果我遇到其他困难,我会回复。由于我在搜索中没有遇到它,Class.query 语法记录在哪里?
    • class.query 语法来自 Flask-SQLAlchemy。如果您使用普通的 SQLAlchemy,则等效语法为 session.query(Class)。见docs.sqlalchemy.org/en/rel_0_9/orm/query.html
    • 抱歉,我仍然无法在我的上下文中完成这项工作。我用一个最小的工作示例扩展了我的原始问题(如果你手头有 python3 和 sqlalchemy)。你能看看这个吗?
    • 很好的答案 @miguel ,但它不能带来子值,例如,我在 parent_table 中有 5 条记录,而 parent 的第二条记录在 child_table 中有一条记录(例如 parent_id : 2) 但是另一个父母的记录还没有记录在child_table 中,那么我如何才能获取包括子记录在内的所有数据,即使另一个没有呢?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-14
    • 1970-01-01
    • 2018-02-21
    • 2018-11-24
    • 2017-09-05
    相关资源
    最近更新 更多