我希望在继承的(mixin)类中有一个"relationship"。
但是,当我创建继承的对象时,关系对象为None。我无法附加。
如何解决此问题?
以下是基于文档的代码
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
一般来说,我应该能够附加到关系对象(目标),但这里我不能,因为它是None。为什么?
之所以值为None,是因为您将其定义为多对一关系。从父级到子级的多对一意味着父级上有一个外键,该外键只能引用一个且只能引用一个子级。如果您希望RefTargetMixin
类的某些内容引用项的集合,那么外键必须位于远程端。
因此,这里的目标是使RefTargetMixin的子类中的任何对象都成为Target的潜在父对象。这种模式被称为多态关联模式。虽然在许多ORM工具包中,通过在Target上声明"多态外键"来提供这一点是很常见的,但这在关系上不是一个好的做法,因此答案是以某种方式使用多个表。在examples/generic_association文件夹中的SQLAlchemy核心中提供了三种场景,包括"带鉴别器的单个关联表"、"每个关联的表"one_answers"每个相关的表"。这里,每个模式都为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
这是正常行为:Foo有一个Target。创建Foo对象时,它还没有Target,因此Foo().target
的值为None
。
如果您希望Foo有多个Target,那么应该在Target中放置一个foo_id
,而不是在Foo中放置target_id
,并使用backref。
此外,在这种情况下,不需要指定主联接。