【问题标题】:Materialized path relationship in declarative SQLAlchemy声明性 SQLAlchemy 中的物化路径关系
【发布时间】:2014-05-28 01:50:38
【问题描述】:

我有一个分层类别模型,其中层次结构是使用物化路径(每级一个字符)维护的:

class Category(Base):
    __tablename__ = 'categories'

    id = Column(SmallInteger, primary_key=True)
    path = Column(String, unique=True, nullable=False)

    # problematic relationship
    all_subcats = relationship('Category', lazy='dynamic', viewonly=True,
                               primaryjoin=foreign(path).like(remote(path).concat('%')))

在尝试定义“所有子类别”关系时,我遇到了一个问题:

sqlalchemy.exc.ArgumentError: Can't determine relationship direction for
relationship 'Category.all_subcats' - foreign key columns within the join
condition are present in both the parent and the child's mapped tables.
Ensure that only those columns referring to a parent column are marked as
foreign, either via the foreign() annotation or via the foreign_keys argument.

SQLAlchemy 很困惑,因为我加入了同一列。我设法找到的所有示例总是加入不同的列。

这种关系可能吗?我想通过这个join查询,所以自定义@property是不行的。

【问题讨论】:

  • 您应该在sqlalchemy mailing list 上报告此类问题。我现在正在为此测试补丁。
  • 哇,这真的很古怪。需要看看我是否可以提交。

标签: python sqlalchemy relationship declarative materialized-path-pattern


【解决方案1】:

使用最新的 git master 或 0.9.5 或更高版本的 SQLAlchemy。那么:

from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class Element(Base):
    __tablename__ = 'element'

    path = Column(String, primary_key=True)

    related = relationship('Element',
                           primaryjoin=
                                remote(foreign(path)).like(
                                        path.concat('/%')),
                           viewonly=True,
                           order_by=path)

e = create_engine("sqlite://", echo=True)
Base.metadata.create_all(e)

sess = Session(e)
sess.add_all([
    Element(path="/foo"),
    Element(path="/foo/bar1"),
    Element(path="/foo/bar2"),
    Element(path="/foo/bar2/bat1"),
    Element(path="/foo/bar2/bat2"),
    Element(path="/foo/bar3"),
    Element(path="/bar"),
    Element(path="/bar/bat1")
])

e1 = sess.query(Element).filter_by(path="/foo/bar2").first()
print [e.path for e in e1.related]

请注意,无论您处理“后代”还是“祖先”,此模型都使用集合。您希望将 remote()foreign() 保持在一起,以便 ORM 将其视为一对多。

【讨论】:

  • 它与 SQLAlchemy git master 一起工作得很好。非常感谢!
  • 有没有办法用这个进行子查询预加载,而不为每个对象发出单独的 SELECT?
  • 如果使用 subqueryload() 会发生什么?它失败?在这种情况下可能需要更多修复,请发布错误报告。
  • @zzzeek 嗯,似乎subqueryload() 有效,但指定lazy="subquery" 无效。我会提交错误报告。
猜你喜欢
  • 1970-01-01
  • 2010-12-19
  • 1970-01-01
  • 2016-07-11
  • 2010-09-20
  • 2017-05-03
  • 2011-02-06
  • 2014-05-06
  • 1970-01-01
相关资源
最近更新 更多