在实例化对象中,具有关系的SQLAlchemy继承是None

时间:2012-07-09 16:12:57

标签: python sqlalchemy mixins single-table-inheritance

我希望在继承(mixin)类中有一个'关系'。

但是,当我创建继承对象时,关系对象为None。我不能追加它。

如何解决此问题?

以下是基于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

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

2 个答案:

答案 0 :(得分:4)

值为None的原因是因为您已将此定义为多对一关系。父对子之间的多对一意味着父母有一个外键,只能引用一个且只有一个孩子。如果你想要类RefTargetMixin的东西来引用一组项目,那么外键必须在远程一侧。

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

例如,这里是你的模型使用“table per association”,在我的视图中,如果你不需要一次查询多种类型的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

答案 1 :(得分:1)

这是正常行为:Foo有一个目标。当您创建Foo对象时,它还没有Target,因此Foo().target的值为None

如果你想要Foo有多个目标,你应该在目标中放置foo_id,而不要在Foo中放置target_id,并使用backref。

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