2016-07-21 3 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에 저장되는 :

나는 같은 선언 항목의 테이블이 있습니다. 몇 가지 개체 유형의 경우 특정 메서드 나 특성을 사용할 수 있어야합니다. 로드 할 나는 모든 전문 항목 (type=='specialitem' 갖는) 싶어, 내 항목을로드 할 때 지금

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" 

:

내가 항목의 서브 클래스로 여러 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