【问题标题】:SQLAlchemy inheritance with relationship is None in instantiated objectSQLAlchemy 继承关系在实例化对象中为 None
【发布时间】:2012-07-09 16:12:57
【问题描述】:

我想在继承的(mixin)类中建立“关系”。

但是,当我创建继承对象时,关系对象为无。我无法附加到它。

我该如何解决这个问题?

这是基于documentation的代码

from sqlalchemy import Column, Integer, String, DateTime, Boolean, BigInteger, Float
from sqlalchemy import ForeignKey
from sqlalchemy.orm import relationship, backref
from sqlalchemy.ext.declarative import declared_attr
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()
class Target(Base):
    __tablename__ = "target"
    id = Column(Integer, primary_key=True)

class RefTargetMixin(object):
    @declared_attr
    def target_id(cls):
        return Column('target_id', ForeignKey('target.id'))

    @declared_attr
    def target(cls):
        return relationship("Target",
            primaryjoin="Target.id==%s.target_id" % cls.__name__
        )

class Foo(RefTargetMixin, Base):
    __tablename__ = 'foo'
    id = Column(Integer, primary_key=True)

print repr(RefTargetMixin.target)
print repr(Foo.target)
print repr(Foo().target)

输出是:

<sqlalchemy.orm.properties.RelationshipProperty object at 0x24e7890>
<sqlalchemy.orm.attributes.InstrumentedAttribute object at 0x24e7690>
None

一般来说,我应该能够附加到关系对象(目标),但在这里我不能,因为它是无。为什么?

【问题讨论】:

    标签: python sqlalchemy mixins single-table-inheritance


    【解决方案1】:

    值为 None 的原因是因为您已将其定义为多对一关系。多对一,from parent-to-child,是指在parent上有一个外键,只能指一个也只有一个孩子。如果您希望 RefTargetMixin 类的东西引用项目集合,那么外键必须在远程端。

    那么这里的目标是让任何作为 RefTargetMixin 子类的对象成为 Target 的潜在父对象。这种模式称为polymorphic association pattern。虽然在许多 ORM 工具包中通过在 Target 上声明“多态外键”来提供这一点很常见,但这并不是一个好的关系实践,因此答案是以某种方式使用多个表。 SQLAlchemy 核心在 examples/generic_association 文件夹中为此提供了三种方案,包括“带鉴别器的单个关联表”、“每个关联的表”和“每个相关的表”。每个模式在此处为 RefTargetMixin 提供相同的声明模式,但表的结构会发生变化。

    例如,这是您的模型使用“每个关联的表”,在我看来,如果您不需要一次查询多种类型的 RefTargetMixin 对象,它往往会扩展得最好(请注意,我按原样使用了该示例,只是改了名字):

    from sqlalchemy.ext.declarative import declarative_base, declared_attr
    from sqlalchemy import create_engine, Integer, Column, \
                        String, ForeignKey, Table
    from sqlalchemy.orm import Session, relationship
    
    class Base(object):
        """Base class which provides automated table name
        and surrogate primary key column.
    
        """
        @declared_attr
        def __tablename__(cls):
            return cls.__name__.lower()
        id = Column(Integer, primary_key=True)
    Base = declarative_base(cls=Base)
    
    class Target(Base):
        pass
    
    class RefTargetMixin(object):
        @declared_attr
        def targets(cls):
            target_association = Table(
                "%s_targets" % cls.__tablename__,
                cls.metadata,
                Column("target_id", ForeignKey("target.id"),
                                    primary_key=True),
                Column("%s_id" % cls.__tablename__,
                                    ForeignKey("%s.id" % cls.__tablename__),
                                    primary_key=True),
            )
            return relationship(Target, secondary=target_association)
    
    class Customer(RefTargetMixin, Base):
        name = Column(String)
    
    class Supplier(RefTargetMixin, Base):
        company_name = Column(String)
    
    engine = create_engine('sqlite://', echo=True)
    Base.metadata.create_all(engine)
    
    session = Session(engine)
    
    session.add_all([
        Customer(
            name='customer 1',
            targets=[
                Target(),
                Target()
            ]
        ),
        Supplier(
            company_name="Ace Hammers",
            targets=[
                Target(),
            ]
        ),
    ])
    
    session.commit()
    
    for customer in session.query(Customer):
        for target in customer.targets:
            print target
    

    【讨论】:

      【解决方案2】:

      这是正常行为:Foo 有一个 Target。当你创建Foo对象时,它还没有Target,所以Foo().target的值为None

      如果您希望 Foo 有多个 Target,则应在 Target 中放置 foo_id,而不是在 Foo 中放置 target_id,并使用 backref。

      另外,在这种情况下,不需要指定主连接。

      【讨论】:

      • 我明白你的提议。但这不是一个合适的解决方案。如果我创建一个类 Bar,它也是 RefTargetMixin 的子类,Target 只指向 Foo。我想要 几个 RefTargetMixin 子类,每个子类都与 Target 具有一对多的关系。
      • 明确一点,您希望RefTargetMixin 的每个子类有多个Target,是吗?
      • 那么我的立场就是立场。在这种情况下,你不能只使用 mixins,你需要使用真正的继承。见docs.sqlalchemy.org/en/rel_0_7/orm/extensions/…
      猜你喜欢
      • 1970-01-01
      • 2012-10-06
      • 1970-01-01
      • 2014-08-02
      • 1970-01-01
      • 1970-01-01
      • 2017-04-05
      • 1970-01-01
      • 2021-12-27
      相关资源
      最近更新 更多