在SQLAlchemy中,“relationship”中的“backref”param是否可以分配一个变量?

2024-03-29 10:41:31 发布

您现在位置:Python中文网/ 问答频道 /正文

我在这里使用连接表继承。在

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,但是它应该能够被无限的子类继承),所以我需要depends_on_who_inherits_me部分变量,或者更具体地说,继承子类的名称,而不是字符串。因此,每个子类都有一个引用第三方类的关系,同时让它以适当的名称引用该特定的子类。在

但是,因为这是在任何方法之外的,所以我不能使用self灵活地引用实例。在

如何实现这一理念?谢谢。在


Tags: 名称stringoncolumncolsome子类class
1条回答
网友
1楼 · 发布于 2024-03-29 10:41:31

实现这一点的一种方法是使用^{}^{}。在

这是混音课:

class Mixin:

    @declared_attr.cascading
    def related_entity(cls):
        if has_inherited_table(cls):
            return relationship(
                'RelatedEntity',
                backref=cls.__name__.lower(),
                uselist=False
            )

declared_attr上的cascading标志将使sqlalchemy尝试在层次结构中的每个类上呈现“mixed-in”属性。或者正如文件所说:

This is a special-use modifier which indicates that a column or MapperProperty-based declared attribute should be configured distinctly per mapped subclass, within a mapped-inheritance scenario.

^{}函数允许我们在mixin中确定我们处理的是BaseEntity还是子类,这样我们只将关系添加到子类上。在

然后将mixin继承到BaseEntity模型中:

^{pr2}$

正如您在您的问题中提到的,您正在使用联接表继承,我已经使用@declared_attr方法在BaseEntity上定义了__mapper_args__,这样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>]

我们不能在BaseModel中定义related_entity()declared_attr方法是因为SQLAlchemy不支持级联,并且不会生成任何关系(因为if has_inherited_table(cls):块阻止BaseModel生成关系)。从the docs

The flag only applies to the use of declared_attr on declarative mixin classes and __abstract__ classes; it currently has no effect when used on a mapped class directly.

相关问题 更多 >