2012-03-07 16 views
12

Tak więc mam kilka tabel przy użyciu SQLAlchemy, które są modelowane jako obiekty, które dziedziczą od wyniku do połączenia z declarative_base(). Tj:Sqlalchemy: unikanie dziedziczenia wielokrotnego i posiadanie abstrakcyjnej klasy bazowej

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

class Table2(Base): 
    # __tablename__ & such here 

Itd Potem chciał mieć jakąś wspólną funkcjonalność dostępną dla każdego z moich klas tabeli DB, the easiest way to do this according to the docs jest po prostu zrobić wielokrotne dziedziczenie:

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 

Rzecz Nie lubię o tym jest A) wiele dziedziczenia w ogóle jest trochę nieprzyjemne (dostaje trudne rozwiązania takie jak wywołania super(), itp.), B) jeśli dodaję nową tabelę muszę pamiętać, aby dziedziczyć po obu Base i CommonRoutines i C) naprawdę to "CommonRoutines" klasa "is-a" rodzaj tabeli w pewnym sensie. Naprawdę to, co jest CommonBase jest abstrakcyjną klasą bazową, która definiuje zestaw procedur, które są wspólne dla wszystkich tabel. Innymi słowy: "jego-a" abstrakcyjny stół.

Więc, co chciałbym to:

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 

Ale to oczywiście nie działa, bo wtedy trzeba A) zdefiniować __tablename__ dla AbstractTable b) aspekt ABC rzeczy powoduje różnego rodzaju bóle głowy, a C) musi wskazywać na pewien rodzaj związku DB między AbstractTable a każdą poszczególną tabelą.

Moje pytanie: czy możliwe jest osiągnięcie tego w rozsądny sposób? Idealnie chciałbym wymusić:

  • Nie wielokrotne dziedziczenie
  • CommonBase/AbstractTable być abstrakcyjne (czyli nie może być instancja)

Odpowiedz

14

Jest całkiem strita do przodu, po prostu zrobić declarative_base() powrót klasa Base, która dziedziczy po parametrze CommonBase przy użyciu parametru cls=. Również pokazano w dokumentach Augmenting The Base. Twój kod może wtedy wyglądać podobnie jak poniżej:

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

Popraw mnie jeśli się mylę, ale te „AbstractTable” spadki powinien przeczytać „baza”, prawda? (tzn. 'klasa Table1 (Base):') –

+0

@AdamParkin: oczywiście. poprawione. – van

+0

Yup, to było dokładnie to, czego szukałem. Dzięki! Jedyną wadą tego podejścia jest to, że teraz klasa '__init__' klasy" CommonBase "nie jest wywoływana (' deklarative_base' tworzy klasę, która nie wywołuje 'super' w jej' __init__', więc Python współpracujące mechanizmy dziedziczenia wielu nie praca). Hmm .... –

2

Można wykorzystać AbstractConcreteBase dokonać Absract modelu podstawowego:

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

wersję sqlalchemy 0.7.3 wprowadzony dyrektywy __abstract__ który jest używany do klas abstrakcyjnych, które nie powinny być mapowane do tabeli bazy danych, nawet jeśli są podklasami sqlalchemy.ext.declarative.api.Base. Więc teraz utworzyć klasę bazową tak:

Base = declarative_base() 

class CommonRoutines(Base): 
    __abstract__ = True 

    id = Column(Integer, primary_key=True) 

    def __init__(self): 
     # ... 

Wskazówki jak CommonRoutines nie posiada atrybut __tablename__. Następnie należy utworzyć podklasy tak:

class Foo(CommonRoutines): 
    __tablename__ = 'foo' 

    name = Column(...) 

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

To będzie mapować do stołu foo i dziedziczyć atrybut id z CommonRoutines.

Źródło i więcej informacji: http://docs.sqlalchemy.org/en/rel_0_7/orm/extensions/declarative.html#abstract

+0

Tego właśnie szukałem. dzięki – pylover

Powiązane problemy