2016-07-21 5 views
12

Verwenden von Python 3.5 und SQLAlchemy 1.0.14 (ORM).SQLAlchemy ORM: Polymorphe Vererbung einzelner Tabellen, mit Fallback zur übergeordneten Klasse, wenn "polymorphic_identity" nicht gefunden wird

Ich habe eine Tabelle mit Elementen als solche deklarieren:

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 

Mein Telefon von vielen verschiedenen Arten sein kann, die Typkennung in type gespeichert werden. Für einige dieser Objekttypen müssen bestimmte Methoden oder Attribute verfügbar sein.

Um das zu erreichen, dass ich versuchte, einzelne Tabelle Vererbung zu verwenden mit mehreren SpecialisedItem als Unterklasse von Item: Jetzt

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" 

wenn ich meine Artikel laden, ich alle Facheinzelteile wünschen würde (mit type=='specialitem') geladen werden Somit würde jeder andere Typwert dazu führen, dass die Elternklasse Item geladen wird. Das funktioniert nicht, ich bekomme AssertionError: No such polymorphic_identity 'normal' is defined beim Laden der Elemente.

würde Ich mag geerbten Klassen vermeiden schaffen, die einfach nichts tun, alle möglichen type Werte abzudecken, statt Item „nicht zuzugeordneten“ type fiel zurück auf die Elternklasse habe.

Gibt es eine Möglichkeit, diesen Effekt zu erzielen?

Minimal Testfall als Referenz:

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) 

Danke,

Guillaume

Antwort

6

Eine Zuordnung von „polymorpher Identität“ Identifikatoren Instanzen Mapper ist in dem polymorphic_map dict gespeichert. Sie können eine benutzerdefinierte polymorphic_map erstellen, die den übergeordneten Klassenzuordner für nicht definierte polymorphe Identitäten zurückgibt.

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

Das funktioniert, danke! Wenn es Ihnen nichts ausmacht, habe ich Ihren Antwortcode aktualisiert, um direkt zu arbeiten. Du wirst die Belohnung bekommen. – Guillaume

Verwandte Themen