2

I would like to have a 'relationship' in an inherited (mixin) class.

However, when I create the inherited object, the relationship object is None. I cannot append to it.

How do I resolve this?

Here is code based upon the 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)

The output is:

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

In general, I should be able to append to the relationship object (target), but here I cannot because it is None. Why?

4

2 回答 2

4

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

因此,这里的目标是使作为 RefTargetMixin 子类的任何对象成为 Target 的潜在父级。这种模式称为多态关联模式。虽然在许多 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
于 2012-07-11T16:55:53.180 回答
1

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

如果你想让 Foo 有多个 Target,你应该foo_id在 Target 中放一个,而不是target_id在 Foo 中放一个,并使用 backref。

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

于 2012-07-09T16:26:51.947 回答