2010-05-18 25 views
7

¿Es posible tener polimorfismo multinivel en SQLAlchemy? He aquí un ejemplo:¿Es posible el polimorfismo multinivel en SQLAlchemy?

class Entity(Base): 
    __tablename__ = 'entities' 
    id = Column(Integer, primary_key=True) 
    created_at = Column(DateTime, default=datetime.utcnow, nullable=False) 
    entity_type = Column(Unicode(20), nullable=False) 
    __mapper_args__ = {'polymorphic_on': entity_type} 

class File(Entity): 
    __tablename__ = 'files' 
    id = Column(None, ForeignKey('entities.id'), primary_key=True) 
    filepath = Column(Unicode(255), nullable=False) 
    file_type = Column(Unicode(20), nullable=False) 
    __mapper_args__ = {'polymorphic_identity': u'file', 'polymorphic_on': file_type) 

class Image(File): 
    __mapper_args__ = {'polymorphic_identity': u'image'} 
    __tablename__ = 'images' 
    id = Column(None, ForeignKey('files.id'), primary_key=True) 
    width = Column(Integer) 
    height = Column(Integer) 

Cuando llamo Base.metadata.create_all(), SQLAlchemy plantea el siguiente error:

IntegrityError: (IntegrityError) entities.entity_type may not be NULL`. 

Este error desaparece si quito el modelo Image y la clave polymorphic_on en File.

¿Qué ofrece?

+0

Puede tener mejor suerte haciendo esto sin la capa declarativa. – joeforker

Respuesta

11

Yes. El problema con su código es que está haciendo de Image un tipo de archivo, cuando debe apuntar a la cabeza del árbol, haciendo de Image un tipo de Entity.

Ejemplo:

from sqlalchemy import (Table, Column, Integer, String, create_engine, 
    MetaData, ForeignKey) 
from sqlalchemy.orm import mapper, create_session 
from sqlalchemy.ext.declarative import declarative_base 

e = create_engine('sqlite:////tmp/foo.db', echo=True) 
Base = declarative_base(bind=e) 

class Employee(Base): 
    __tablename__ = 'employees' 

    employee_id = Column(Integer, primary_key=True) 
    name = Column(String(50)) 
    type = Column(String(30), nullable=False) 

    __mapper_args__ = {'polymorphic_on': type} 

    def __init__(self, name): 
     self.name = name 

class Manager(Employee): 
    __tablename__ = 'managers' 
    __mapper_args__ = {'polymorphic_identity': 'manager'} 

    employee_id = Column(Integer, ForeignKey('employees.employee_id'), 
     primary_key=True) 
    manager_data = Column(String(50)) 

    def __init__(self, name, manager_data): 
     super(Manager, self).__init__(name) 
     self.manager_data = manager_data 

class Owner(Manager): 
    __tablename__ = 'owners' 
    __mapper_args__ = {'polymorphic_identity': 'owner'} 

    employee_id = Column(Integer, ForeignKey('managers.employee_id'), 
     primary_key=True) 
    owner_secret = Column(String(50)) 

    def __init__(self, name, manager_data, owner_secret): 
     super(Owner, self).__init__(name, manager_data) 
     self.owner_secret = owner_secret 

Base.metadata.drop_all() 
Base.metadata.create_all() 

s = create_session(bind=e, autoflush=True, autocommit=False)  
o = Owner('nosklo', 'mgr001', 'ownerpwd') 
s.add(o) 
s.commit() 
0

No es posible (véase SQL ALchemy doc):

Currently, only one discriminator column may be set, typically on the base-most class in the hierarchy. “Cascading” polymorphic columns are not yet supported.

por lo que debe seguir @nosklo propuesta para cambiar su patrón de herencia.

Cuestiones relacionadas