Sqlalchemy: evitar la herencia múltiple y tener una clase base abstracta

Así que tengo un montón de tablas que usan SQLAlchemy que se modelan como objetos que heredan del resultado a una llamada a declarative_base() . Es decir:

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

Etc. Luego quise tener alguna funcionalidad común disponible para cada una de mis clases de tabla de base de datos, la forma más sencilla de hacer esto de acuerdo con los documentos 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 de esto es A) la herencia múltiple en general es un poco complicada (se complica la resolución de cosas como super() llamadas super() , etc.), B) si agrego una nueva tabla que debo recordar heredar de ambos Base y CommonRoutines , y C) realmente que 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 y rutinas que son comunes a todas las tablas. Dicho de otra manera: “es-una” tabla abstracta.

Entonces, lo que me gustaría es esto:

 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 tengo que A) definir un __tablename__ para AbstractTable , B) el aspecto ABC de las cosas causa todo tipo de dolores de cabeza, y C) tiene que indicar algún tipo de relación DB entre AbstractTable y cada uno mesa individual

Así que mi pregunta: ¿es posible lograr esto de una manera razonable? Idealmente me gustaría hacer cumplir:

  • Sin herencia múltiple
  • CommonBase / AbstractTable ser abstracto (es decir, no puede ser instanciado)

Es bastante sencillo, simplemente hace declarative_base() para devolver una clase Base que hereda de su CommonBase usando el parámetro cls= . También se muestra en Aumentar la base de documentos. Su código podría verse 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 

SQLAlchemy versión 0.7.3 introdujo la directiva __abstract__ que se usa para las clases abstractas que no deben asignarse a una tabla de base de datos, aunque sean subclases de sqlalchemy.ext.declarative.api.Base . Así que ahora creas una clase base como esta:

 Base = declarative_base() class CommonRoutines(Base): __abstract__ = True id = Column(Integer, primary_key=True) def __init__(self): # ... 

Observe que CommonRoutines no tiene un atributo __tablename__ . Luego crea subclases como esta:

 class Foo(CommonRoutines): __tablename__ = 'foo' name = Column(...) def __init__(self, name): super().__init__() self.name = name # ... 

Esto se asignará a la tabla foo y heredará el atributo id de CommonRoutines .

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

Puede utilizar AbstractConcreteBase para hacer un modelo base de 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