2017-08-30 28 views
0

私はこの質問を書くのに苦労しました。それは複雑で珍しいユースケースになるかもしれません。SQLAlchemyのクラス継承階層をサブクラス化

共通のデータベーススキーマとコア機能を維持する1つのプロジェクトに複数の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クラスをリファクタリングしたいと思います。たとえば、EmailCampaignDBPushCampaignDBの基本クラスをCampaignDBにしたいとします。次に、インポートプロジェクトで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」コンテキストでこれを達成するためのより良い方法はありますか?

+0

[この質問](https://stackoverflow.com/questions/37621423)はあなたの正確な使用例です。 – univerio

答えて

0

問題の考え方を変えることで、この作業を行う方法を見つけることができました。私はCampaignというサブクラスを作成してクエリしようとしましたが、CampaignDBです。また、サブクラス化を容易にする宣言型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:多型関連の再割り当てアイデンティティ "のために、私はまだ良い方法があるように感じる。

関連する問題