我希望在继承(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
一般来说,我应该能够追加到关系对象(目标),但在这里我不能,因为它是无。为什么呢?
答案 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。
此外,在这种情况下,不需要指定主要连接。