5

Estoy utilizando la herencia de tabla concreta con SQLAlchemy. En la clase de modelo de estilo declartivo, lo configuré con éxito.SQLAlchemy Relación polimórfica con la herencia concreta

Mi código al igual que:

class Entry(AbstractConcreteBase, db.Model): 
    """Base Class of Entry.""" 

    id = db.Column(db.Integer, primary_key=True, nullable=False) 
    created = db.Column(db.DateTime, nullable=False) 
    post_id = declared_attr(lambda c: db.Column(db.ForeignKey("post.id"))) 
    post = declared_attr(lambda c: db.relationship("Post", lazy="joined")) 

    @declared_attr 
    def __tablename__(cls): 
     return cls.__name__.lower() 

    @declared_attr 
    def __mapper_args__(cls): 
     # configurate subclasses about concrete table inheritance 
     return {'polymorphic_identity': cls.__name__, 
       'concrete': True} if cls.__name__ != "Entry" else {} 

class TextEntry(Entry): 
    """Text and Article Entry.""" 

    text = db.deferred(db.Column(db.Text, nullable=False)) 

class PhotoEntry(Entry): 
    """Photo Entry.""" 

    path = db.deferred(db.Column(db.String(256), nullable=False)) 

Funciona bien mientras se prueba entonces en el soporte:

>>> from models.entry import Entry 
>>> 
>>> Entry.query.all() 
[<PhotoEntry 'Title' created by tonyseek>, 
<PhotoEntry 'TITLE 2' created by tonyseek>, 
<PhotoEntry 'Title 3' created by tonyseek>, 
<PhotoEntry 'Title 4' created by tonyseek>, 
<TextEntry 'Title' created by tonyseek>] 

Entonces caen en problemas al establecer la relación en otros modelos. Cada entrada tiene una clave externa post_id para unirse al modelo Post, pero no pude definir la referencia posterior en Post. Esto no puede funcionar:

class Post(db.Model): 
    """An Post.""" 

    id = db.Column(db.Integer, primary_key=True, nullable=False) 
    description = db.Column(db.Unicode(140), nullable=False) 
    entries = db.relationship(Entry, lazy="dynamic") 

Se lanza una excepción y le dijo:

InvalidRequestError: One or more mappers failed to initialize - can't proceed with initialization of other mappers. Original exception was: Class 'models.entry.Entry' is not mapped.

Obviamente el Entry es una clase abstracta, que no pudo ser mapeado a un verdadero existe mesa. El documento en el sitio web oficial tiene un ejemplo, pero su clase base no es abstracta. Ahora, ¿cómo debo hacer para establecer la relación polimórfica con un modelo abstracto?

Respuesta

7

He encontrado la razón del problema y su solución.

De acuerdo con el documento del sitio web oficial de sqlalchemy, la clase abstracta podría ser una clase mapeada, porque la función polymorphic_union podría crear una tabla virtual.

Estoy utilizando el modelo de estilo declartivo, no compilar el mapeador a mano, por lo que la tabla virtual pjoin no debe crearse a mano. La clase base AbstractConcreteBase tiene un método __delcare_last__ que crearía el pjoin con la función polymorphic_union, pero se invocaría mientras ocurriera el evento after_configured.

La relación con Entry en Post se creó después de generarse la clase Post, en esta ocasión el evento after_configured no se han disparado, por lo __delcare_last__ función no han creado la tabla virtual pjoin y cartografiado en Entry. Entonces la excepción "Class 'models.entry.Entry' no está mapeada". se levantará

Ahora, refactorizar el modelo Post, deje que se crea la relación con Entry en __delcare_last__ función, entonces será el éxito debido al evento disparado y el mapeado Entry.

Mi nueva clase implementado como esto:

class Post(db.Model): 
    """An Post.""" 

    id = db.Column(db.Integer, primary_key=True, nullable=False) 
    description = db.Column(db.Unicode(140), nullable=False) 

    @classmethod 
    def __declare_last__(cls): 
     cls.entries = db.relationship(Entry, viewonly=True) 

    def attach_entries(self, entries): 
     """Attach Entries To This Post. 

     Example: 
      >>> post = Post("An Interesting News", "Wow !!!") 
      >>> text_entry = TextEntry(*t_args) 
      >>> photo_entry = PhotoEntry(*p_args) 
      >>> post.attach_entries([text_entry, photo_entry]) 
      >>> len(post.entries) 
      2 
      >>> db.session.commit() 
      >>> 
     """ 
     for entry in entries: 
      self.entries.append(entry) 
      entry.post = self 
      db.session.add(entry) 
Cuestiones relacionadas