我在这里使用联接表继承。
class BaseEntity(Base):
some_col = Column(String)
base_relationship = relationship("some_relationship", backref="depends_on_who_inherits_me")
class SubEntity(BaseEntity):
some_unique_col = Column(String)
由于特定的backref名称仅在运行时才知道(在这种情况下,它应该为SubEntity
,但是它应该可以被无限的子类继承),因此我需要{{1} }组成一个变量,或更具体地说,继承子类的名称,而不是字符串。因此,每个子类都有一个引用第三方类的关系,同时又用其适当的名称引用该特定子类。
但是,由于这是任何方法之外的,所以我不能使用depends_on_who_inherits_me
灵活地引用实例。
如何实现这个想法?谢谢。
答案 0 :(得分:1)
一种实现此目标的方法是使用Mixin
的declared_attr.cascading
。
这是mixin类:
class Mixin:
@declared_attr.cascading
def related_entity(cls):
if has_inherited_table(cls):
return relationship(
'RelatedEntity',
backref=cls.__name__.lower(),
uselist=False
)
cascading
上的declared_attr
标志将使sqlalchemy尝试在层次结构中的每个类上呈现“ mixed in”属性。或者如文档所述:
这是一个特殊用途的修饰符,它指示列或 应该配置基于MapperProperty的声明属性 在映射继承场景中,每个映射子类都有明显区别。
has_inherited_table()
函数使我们能够在mixin中确定是要处理BaseEntity
还是子类,因此我们仅将关系添加到子类上。
然后将mixin继承到BaseEntity
模型中:
class BaseEntity(Base, Mixin):
id = sa.Column(sa.Integer, primary_key=True)
related_id = sa.Column(
sa.Integer, sa.ForeignKey('relatedentity.id'))
discriminator = sa.Column(sa.String)
@declared_attr
def __mapper_args__(cls):
if has_inherited_table(cls):
args = {'polymorphic_identity': cls.__name__.lower()}
else:
args = {'polymorphic_on': cls.discriminator}
return args
正如您在问题中提到的那样,您正在使用联接表继承,我已经使用__mapper_args__
方法在BaseEntity
上定义了@declared_attr
,以便polymorphic_identity
可以根据子类的类名自动生成。
因此,使用此配置,BaseEntity
的每个子类将在以该子类命名的RelatedEntity
上应用一个关系属性。这是完整的工作示例:
import sqlalchemy as sa
from sqlalchemy.ext.declarative import (declarative_base, declared_attr,
has_inherited_table)
from sqlalchemy.orm import relationship, sessionmaker
class BaseClass:
@declared_attr
def __tablename__(cls):
return cls.__name__.lower()
Base = declarative_base(cls=BaseClass)
engine = sa.create_engine('sqlite://', echo=False)
Session = sessionmaker(bind=engine)
class Mixin:
@declared_attr.cascading
def related_entity(cls):
if has_inherited_table(cls):
return relationship(
'RelatedEntity',
backref=cls.__name__.lower(),
uselist=False
)
class BaseEntity(Base, Mixin):
id = sa.Column(sa.Integer, primary_key=True)
related_id = sa.Column(
sa.Integer, sa.ForeignKey('relatedentity.id'))
discriminator = sa.Column(sa.String)
@declared_attr
def __mapper_args__(cls):
if has_inherited_table(cls):
args = {'polymorphic_identity': cls.__name__.lower()}
else:
args = {'polymorphic_on': cls.discriminator}
return args
class RelatedEntity(Base):
""" Class that is related to all `BaseEntity` subclasses"""
id = sa.Column(sa.Integer, primary_key=True)
class SubEntity(BaseEntity):
""" Will generate `RelatedEntity.subentity`"""
id = sa.Column(sa.Integer, sa.ForeignKey('baseentity.id'),
primary_key=True)
class OtherEntity(BaseEntity):
""" Will generate `RelatedEntity.otherentity`"""
id = sa.Column(sa.Integer, sa.ForeignKey('baseentity.id'),
primary_key=True)
if __name__ == '__main__':
Base.metadata.drop_all(engine)
Base.metadata.create_all(engine)
s = Session()
rel_inst = RelatedEntity()
s.add(rel_inst)
rel_inst.subentity.append(SubEntity())
rel_inst.otherentity.append(OtherEntity())
s.commit()
print(rel_inst.subentity, rel_inst.otherentity)
# [<__main__.SubEntity object at 0x0000023487D42C18>] [<__main__.OtherEntity object at 0x0000023487D60278>]
我们无法在related_entity()
中定义declared_attr
BaseModel
方法的原因是因为SQLAlchemy不会遵循级联,并且不会生成任何关系(因为{{1 }}阻止if has_inherited_table(cls):
生成一个)。来自the docs:
该标志仅适用于在声明性mixin上使用clarified_attr 类和
BaseModel
类;目前使用时无效 直接在映射的类上。