【问题标题】:Subclassing Class Inheritance Hierarchies from SQLAlchemy从 SQLAlchemy 继承类继承层次结构
【发布时间】:2017-08-30 18:46:56
【问题描述】:

我很难写出这个问题。它涉及一个可能复杂且不常见的用例。

我在一个项目中定义了几个 ORM 类,它们负责维护一个通用的数据库模式和核心功能。例如,假设这是model.email 模块。

from sqlalchemy import Column, Index, ForeignKey
from sqlalchemy import Boolean, Integer, Text
from . import Base


class CampaignDB(Base):
    """
    Note: this basic database mapper class is expected to be extended.
    When sub-classing, be mindful to override mappings to other extended classes.
    """   
    __tablename__ = 'campaigns'

    audience_id = Column(Integer, ForeignKey("audiences.id"))
    active = Column(Boolean)
    name = Column(Text)

这些 ORM 类作为包导入到其他几个项目中。在某些情况下,这些 ORM 类被子类化以提供额外的功能。例如,这里的CampaignDB 类被子类化以提供在特定项目上下文中发送电子邮件的支持。

from model.email import CampaignDB

class Campaign(CampaignDB):
    """
    Provides sending capability to the email campaign ORM class.
    """

    def __init__(self, audience_id=None, active=None, name=None):
        self.audience_id = audience_id
        self.active = active
        self.name = name

    def send(self):
        print("send emails to the audience")

现在我想使用 SQLAlchemy 的类继承层次结构将 CampaignDB 和子类 Campaign 类重构为多态基础。例如,我想让CampaignDB 成为EmailCampaignDBPushCampaignDB 的基类。然后我想在导入项目中分别扩展EmailCampaignDBPushCampaignDB,例如EmailCampaignPushCampaign。但是,我仍然希望能够查询 Campaign 并返回 EmailCampaignPushCampaign 的实例。

我已多次尝试解决此问题,但遇到了问题。特别是,session.query(Campaign).all() 不返回任何结果,因为 SQLAlchemy 似乎不将其视为基类。生成的 SQL 有以下 WHERE 子句:WHERE email.campaigns.type IN (NULL)

这是我正在尝试的要点。

class CampaignDB(Base):
    """
    Note: this basic database mapper class is expected to be extended.
    When sub-classing, be mindful to override mappings to other extended classes.
    """
    __tablename__ = 'campaigns'

    audience_id = Column(Integer, ForeignKey("audiences.id"))
    active = Column(Boolean)
    name = Column(Text)
    type = Column(String(16))

    __mapper_args__ = {
        'polymorphic_on': type
    }


class EmailCampaignDB(CampaignBaseDB):
    __mapper_args__ = {
        'polymorphic_identity': 'email'
    }


class PushCampaignDB(CampaignBaseDB):
    __mapper_args__ = {
        'polymorphic_identity': 'push'
    }

    def send(self):
        print("send push notifications to the audience")

class Campaign(CampaignDB):

    pass

class EmailCampaign(EmailCampaignDB):

    def send(self):
        print("send emails to the audience")

class PushCampaign(PushCampaignDB):

    def send(self):
        print("send push notifications to the audience")

这可能吗?在这种“打包的 ORM”上下文中是否有更好的方法来实现这一点?

【问题讨论】:

标签: python sqlalchemy


【解决方案1】:

我设法通过改变我对问题的看法来找到一种方法来完成这项工作。我放弃了尝试创建和查询CampaignDBCampaign 子类。我还使用了声明式 API,它似乎有助于子类化 EmailCampaignDBPushCampaignDB

核心项目中的model.email模块:

from sqlalchemy.ext.declarative import declared_attr
from sqlalchemy import Column, Index, ForeignKey
from sqlalchemy.orm import relationship
from sqlalchemy import Boolean, Integer, Text
from . import Base

class CampaignBaseDB(Base):
    """
    Note: this basic database mapper class is expected to be extended.
    When sub-classing, be mindful to override mappings to other extended classes.
    """
    __tablename__ = 'campaign_bases'
    @declared_attr
    def __mapper_args__(cls):
        return {
            'polymorphic_on': cls.type,
        }

    audience_id = Column(Integer, ForeignKey("audiences.id"))
    active = Column(Boolean)
    name = Column(Text)
    type = Column(String(16))


class EmailCampaignDB(CampaignBaseDB):

    @declared_attr
    def __mapper_args__(cls):
        return {
            'polymorphic_identity': 'email'
        }


class PushCampaignDB(CampaignBaseDB):

    @declared_attr
    def __mapper_args__(cls):
        return {
            'polymorphic_identity': 'push'
        }

子类化导入项目中的活动类:

from model.email import EmailCampaignDB, PushCampaignDB

class EmailCampaign(EmailCampaignDB):

    def send(self):
        print("send emails to the audience")


class PushCampaign(PushCampaignDB):

    def send(self):
        print("send push notifications to the audience")

多态查询:

for campaign in db.query(CampaignBaseDB).all():
    campaign.send()

#> send emails to the audience
#> send push notifications to the audience

这种方法确实会导致“SAWarning:为身份重新分配多态关联”,所以我仍然觉得会有更好的方法。

【讨论】:

    猜你喜欢
    • 2011-06-16
    • 2014-12-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-18
    • 1970-01-01
    • 1970-01-01
    • 2020-10-24
    相关资源
    最近更新 更多