2012-03-07 10 views
12

Quindi ho un mucchio di tabelle che usano SQLAlchemy che sono modellate come oggetti che ereditano dal risultato in una chiamata a declarative_base(). Vale a dire:Sqlalchemy: evitare l'ereditarietà multipla e avere una classe di base astratta

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

class Table2(Base): 
    # __tablename__ & such here 

ecc Poi volevo avere alcune funzionalità comuni a disposizione di ciascuna delle mie classi della tabella DB, the easiest way to do this according to the docs è quello di fare proprio l'ereditarietà multipla:

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 

La cosa non mi piace A proposito di questo è A) ereditarietà multipla in generale è un po 'icky (diventa difficile risolvere le cose come super() chiamate, ecc), B) se aggiungo una nuova tabella che devo ricordare di ereditare da entrambi Base e CommonRoutines e C) davvero quello "CommonRoutines" classe "is-a" tipo di tabella in un certo senso. In realtà ciò che è CommonBase è una classe base astratta che definisce un insieme di routine di campi & comuni a tutte le tabelle. Detto in altro modo: "è un tavolo astratto".

Quindi, quello che vorrei è questa:

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 

Ma questo, naturalmente, non funziona, come ho poi devo A) definire un __tablename__ per AbstractTable, B) l'aspetto delle cose ABC causa tutti i tipi di mal di testa, e C) devono indicare una sorta di relazione DB tra AbstractTable e ogni singola tabella.

Quindi la mia domanda: è possibile raggiungere questo in un modo ragionevole? Idealmente mi piacerebbe far rispettare:

  • No ereditarietà multipla
  • CommonBase/AbstractTable essere astratto (cioè non è possibile creare un'istanza)

risposta

14

è piuttosto straigh-forward, basta fare declarative_base() per tornare una classe Base che eredita dal tuo CommonBase utilizzando il parametro cls=. Mostra anche nei documenti Augmenting The Base. Il vostro codice potrebbe quindi essere simile al seguito:

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

correggetemi se sbaglio, ma quelle eredità "AbstractTable" dovrebbe leggere "Base", giusto? (es. 'class Table1 (Base):') –

+0

@AdamParkin: ovviamente. corretto. – van

+0

Sì, questo era esattamente quello che stavo cercando. Grazie! L'unico svantaggio di questo approccio è che ora non viene chiamato il '' Bite_base' della classe 'CommonBase'' '___init__' '(' declarative_base' produce una classe che non chiama' super' nel suo '__init__' così i meccanismi multipli di ereditarietà cooperativa di Python non lavoro). Hmm .... –

2

È possibile utilizzare AbstractConcreteBase per fare un modello 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

versione SQLAlchemy 0.7.3 ha introdotto la direttiva __abstract__ che viene utilizzato per le classi astratte che non deve essere mappato a una tabella di database, anche se sono sottoclassi di sqlalchemy.ext.declarative.api.Base. Così ora si crea una classe di base come questo:

Base = declarative_base() 

class CommonRoutines(Base): 
    __abstract__ = True 

    id = Column(Integer, primary_key=True) 

    def __init__(self): 
     # ... 

Notate come CommonRoutines non hai un attributo __tablename__. Quindi creare sottoclassi come questo:

class Foo(CommonRoutines): 
    __tablename__ = 'foo' 

    name = Column(...) 

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

Questo sarà mappare la tabella foo ed ereditare l'attributo id da CommonRoutines.

Fonte e ulteriori informazioni: http://docs.sqlalchemy.org/en/rel_0_7/orm/extensions/declarative.html#abstract

+0

Questo è quello che ho effettivamente cercato. Grazie – pylover

Problemi correlati