2016-07-21 7 views
12

Python 3.5およびSQLAlchemy 1.0.14(ORM)の使用。SQLAlchemy ORM: "polymorphic_identity"が見つからない場合、親クラスへのフォールバックを伴う多相単一テーブル継承

私はそのように宣言項目のテーブルがあります。

from sqlalchemy.ext.declarative.api import declarative_base 

Base = declarative_base() 

class Item(Base): 
    __tablename__ = 'items' 

    id = Column(Integer, primary_key=True) 
    type = Column(String) 
    # other non relevant attributes 

を私のアイテムは、多くの異なる種類のtypeに格納されているタイプ識別子することができます。 これらのオブジェクトタイプのうちのいくつかについては、特定のメソッドまたは属性を使用可能にする必要があります。

class Item(Base): 
    __tablename__ = 'items' 

    id = Column(Integer, primary_key=True) 
    type = Column(String, index=True) 
    # other non relevant attributes 

    __mapper_args__ = { 
     'polymorphic_on': type, 
    } 

class SpecialisedItem(Base): 
    __mapper_args__ = { 
     'polymorphic_identity': 'specialitem', 
    } 

    def specialised_method(self): 
     return "I am special" 

今、私は私の項目を読み込むとき、私は(type=='specialitem'を持つ)すべての特殊なアイテムがロードされるようにしたいと思います:私はアイテムのサブクラスとして、いくつかのSpecialisedItemで単一テーブル継承を使用しようとしたことを達成するために

他の型の値を指定すると、親クラスItemがロードされます。 それは動作しません、私はAssertionError: No such polymorphic_identity 'normal' is definedアイテムを読み込むときに取得します。

私の代わりに、親クラスItemにフォールバックtype「マップされていない」を有する、ちょうどすべての可能なtype値をカバーするために何もしない継承したクラスを作成しないようにしたいと思います。

この効果を得る方法はありますか?参考のため

最小限のテストケース:

from sqlalchemy.engine import create_engine 
from sqlalchemy.ext.declarative.api import declarative_base 
from sqlalchemy.orm.session import sessionmaker 
from sqlalchemy.sql.schema import Column 
from sqlalchemy.sql.sqltypes import Integer, String 


Base = declarative_base() 

class Item(Base): 
    __tablename__ = 'items' 

    id = Column(Integer, primary_key=True) 
    type = Column(String, index=True) 
    # other non relevant attributes 

    __mapper_args__ = { 
     'polymorphic_on': type, 
    } 

class SpecialisedItem(Item): 
    __mapper_args__ = { 
     'polymorphic_identity': 'special', 
    } 

    specialAttribute = Column(String) 

    def specialised_method(self): 
     return "I am special" 


engine = create_engine("sqlite:///:memory:") 
Base.metadata.create_all(engine) 
Session = sessionmaker(bind=engine) 
session = Session() 

session.add(Item(type='normal')) 
session.add(Item(type='special')) 
session.commit() 
# loading only specialized items works 
for item in session.query(Item).filter_by(type="special"): 
    print(item.specialised_method()) 

# loading other items fails 
for item in session.query(Item): 
    print(item.type) 

おかげで、

ギョーム

答えて

6

インスタンスマッパーする「多型同一性」識別子のマッピングはpolymorphic_map辞書に格納されています。定義されていないポリモーフィックIDの親クラスマッパーを返すカスタムpolymorphic_mapを作成することができます。

from sqlalchemy.engine import create_engine 
from sqlalchemy.ext.declarative.api import declarative_base 
from sqlalchemy.orm.session import sessionmaker 
from sqlalchemy.sql.schema import Column 
from sqlalchemy.sql.sqltypes import Integer, String 
from sqlalchemy import event 

Base = declarative_base() 

class Item(Base): 
    __tablename__ = 'items' 

    id = Column(Integer, primary_key=True) 
    type = Column(String, index=True) 
    # other non relevant attributes 

    __mapper_args__ = { 
     'polymorphic_on': type, 
    } 

class SpecialisedItem(Item): 
    __mapper_args__ = { 
     'polymorphic_identity': 'special', 
    } 

    specialAttribute = Column(String) 

    def specialised_method(self): 
     return "I am special" 

#http://docs.sqlalchemy.org/en/rel_1_1/orm/events.html#sqlalchemy.orm.events.MapperEvents.mapper_configured 
@event.listens_for(Item, 'mapper_configured') 
def receive_mapper_configured(mapper, class_): 
    class FallbackToParentPolymorphicMap(dict): 
     def __missing__(self, key): 
      # return parent Item's mapper for undefined polymorphic_identity 
      return mapper 

    new_polymorphic_map = FallbackToParentPolymorphicMap() 
    new_polymorphic_map.update(mapper.polymorphic_map) 
    mapper.polymorphic_map = new_polymorphic_map 

    # for prevent 'incompatible polymorphic identity' warning, not necessarily 
    mapper._validate_polymorphic_identity = None 

engine = create_engine("sqlite:///:memory:") 
Base.metadata.create_all(engine) 
Session = sessionmaker(bind=engine) 
session = Session() 

session.add(Item(type='normal')) 
session.add(Item(type='special')) 
session.commit() 
# loading only specialized items works 
for item in session.query(Item).filter_by(type="special"): 
    print(item.specialised_method()) 

# loading other items fails 
for item in session.query(Item): 
    print(item.type) 
+0

ありがとうございました!あなたが気にしないなら、私は自分のレスポンスコードを更新して直接作業しています。あなたは報酬を得るでしょう。 – Guillaume

関連する問題