2012-03-07 19 views
12

Tengo un conjunto de tablas con SQLAlchemy que se modelan como objetos que heredan del resultado a una llamada al declarative_base(). Es decir:Sqlalchemy: evitar la herencia múltiple y tener clase base abstracta

Base = declarative_base() 
class Table1(Base): 
    # __tablename__ & such here 

class Table2(Base): 
    # __tablename__ & such here 

etc, entonces yo quería tener algunas funciones comunes disponibles para cada una de mis clases de la tabla db, the easiest way to do this according to the docs es simplemente hacer herencia múltiple:

Base = declarative_base() 

class CommonRoutines(object): 
    @classmethod 
    def somecommonaction(cls): 
     # body here 

class Table1(CommonRoutines, Base): 
    # __tablename__ & such here 

class Table2(CommonRoutines, Base): 
    # __tablename__ & such here 

Lo que no me gusta sobre esto es A) la herencia múltiple en general es un poco asquerosa (se complica al resolver cosas como llamadas super(), etc.), B) si agrego una nueva tabla, tengo que recordar heredar tanto de Base como de CommonRoutines, y C) realmente eso La clase "CommonRoutines" es un tipo de tabla en cierto sentido. Realmente lo que CommonBase es es una clase base abstracta que define un conjunto de campos & rutinas que son comunes a todas las tablas. Dicho de otra manera: "its-a" table abstract.

Por lo tanto, lo que me gustaría es la siguiente:

Base = declarative_base() 

class AbstractTable(Base): 
    __metaclass__ = ABCMeta # make into abstract base class 

    # define common attributes for all tables here, like maybe: 
    id = Column(Integer, primary_key=True) 

    @classmethod 
    def somecommonaction(cls): 
     # body here 

class Table1(AbstractTable): 
    # __tablename__ & Table1 specific fields here 

class Table2(AbstractTable): 
    # __tablename__ & Table2 specific fields here 

Pero esto por supuesto no funciona, ya que luego tienen que A) definir un __tablename__ para AbstractTable, B) el aspecto de las cosas ABC causa todo tipo de dolores de cabeza, y C) tiene que indicar algún tipo de relación de DB entre AbstractTable y cada tabla individual.

Entonces mi pregunta: ¿es posible lograr esto de una manera razonable? Lo ideal sería hacer cumplir:

  • Sin herencia múltiple
  • CommonBase/AbstractTable ser abstracta (es decir, no se pueden crear instancias)

Respuesta

14

es bastante straigh de avance, que acaba de hacer declarative_base() para volver una clase Base que hereda de su CommonBase utilizando el parámetro cls=. También se muestra en Augmenting The Base documentos. El código podría entonces ser similar a continuación:

class CommonBase(object): 
    @classmethod 
    def somecommonaction(cls): 
     # body here 

Base = declarative_base(cls=CommonBase) 

class Table1(Base): 
    # __tablename__ & Table1 specific fields here 

class Table2(Base): 
    # __tablename__ & Table2 specific fields here 
+0

corrígeme si me equivoco, pero esas herencias "AbstractTable" debe decir "base", ¿verdad? (es decir, 'clase Table1 (Base):') –

+0

@AdamParkin: por supuesto. corregido – van

+0

Sí, esto era exactamente lo que estaba buscando. ¡Gracias! El único inconveniente del enfoque es que ahora el '' __init__' de la clase 'CommonBase' no se llama (' declarative_base' produce una clase que no llama 'super' en su' __init__' por lo que los mecanismos cooperativos de herencia múltiple de Python no trabajo). Hmm ... –

2

Puede utilizar AbstractConcreteBase para hacer un modelo de base absract:

from sqlalchemy.ext.declarative import AbstractConcreteBase 


class AbstractTable(AbstractConcreteBase, Base): 
    id = db.Column(db.Integer, primary_key=True) 

    @classmethod 
    def somecommonaction(cls): 
     # body here 
26

versión 0.7.3 SQLAlchemy introdujo la directiva __abstract__ que se utiliza para las clases abstractas que no debe asignarse a una tabla de base de datos, aunque son subclases de sqlalchemy.ext.declarative.api.Base. Por lo que ahora se crea una clase base de la siguiente manera:

Base = declarative_base() 

class CommonRoutines(Base): 
    __abstract__ = True 

    id = Column(Integer, primary_key=True) 

    def __init__(self): 
     # ... 

Observe cómo CommonRoutines no tiene un atributo __tablename__. A continuación, crear subclases de esta manera:

class Foo(CommonRoutines): 
    __tablename__ = 'foo' 

    name = Column(...) 

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

Esto asociará a la mesa foo y heredar el atributo de idCommonRoutines.

Fuente y más información: http://docs.sqlalchemy.org/en/rel_0_7/orm/extensions/declarative.html#abstract

+0

Esto fue lo que realmente busqué. Gracias – pylover

Cuestiones relacionadas