从sqlalchemy的子类别继承层次结构



我挣扎着写这个问题。它进入了可能是复杂且罕见的用例。

我已经在一个项目中定义了几个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 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"上下文中,有更好的方法可以实现这一目标吗?

我设法找到了一种方法来通过改变我对问题的看法来进行这项工作。我放弃了尝试创建和查询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:重新分配身份的多态关联",所以我仍然觉得有更好的方法。

最新更新