2016-07-21 15 views
12

Sử dụng Python 3.5 và SQLAlchemy 1.0.14 (ORM).SQLAlchemy ORM: Thừa kế bảng đơn đa hình, với dự phòng cho lớp cha nếu không tìm thấy "polymorphic_identity"

Tôi có một bảng của nội dung kê khai như vậy:

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 

My Items có thể có nhiều loại khác nhau, nhận dạng loại được lưu trữ trong type. Đối với một vài loại đối tượng, tôi cần phải có các phương thức hoặc thuộc tính cụ thể có sẵn.

Để đạt được điều đó tôi cố gắng sử dụng thừa kế bảng duy nhất với một số SpecialisedItem như lớp con của Item:

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" 

Bây giờ khi tôi tải các mặt hàng của tôi, tôi muốn tất cả các mục chuyên ngành (có type=='specialitem') để được nạp như vậy, trong khi bất kỳ giá trị kiểu nào khác sẽ dẫn đến lớp cha mẹ Item đang được tải. Điều đó không hiệu quả, tôi nhận được AssertionError: No such polymorphic_identity 'normal' is defined khi tải các mục.

Tôi muốn tránh tạo các lớp thừa kế không làm gì để trang trải tất cả các giá trị type có thể có, thay vì có "chưa được ánh xạ" type quay trở lại lớp cha Item.

Có cách nào để đạt được hiệu ứng đó không?

trường hợp thử nghiệm tối thiểu để tham khảo:

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) 

Cảm ơn,

Guillaume

Trả lời

6

Một bản đồ của “bản sắc đa hình” định danh để Mapper trường hợp được lưu trữ trong polymorphic_map dict. Bạn có thể tạo tùy chỉnh polymorphic_map sẽ trả về trình ánh xạ lớp cha cho các định danh đa hình không xác định.

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

Điều đó hoạt động, cảm ơn! Nếu bạn không phiền, tôi đã cập nhật mã phản hồi của bạn để hoạt động trực tiếp. Bạn sẽ nhận được phần thưởng. – Guillaume

Các vấn đề liên quan