2012-03-07 3 views
12

Итак, у меня есть куча таблиц с использованием SQLAlchemy, которые моделируются как объекты, которые наследуют результат до вызова declarative_base(). То есть:Sqlalchemy: избегать множественного наследования и иметь абстрактный базовый класс

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

class Table2(Base): 
    # __tablename__ & such here 

Etc. Я тогда хотел иметь некоторые общие функциональные возможности, доступные для каждого из моих БД классов таблиц, the easiest way to do this according to the docs это просто сделать множественное наследование:

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 

Что мне не нравится об этом есть A) множественное наследование в целом немного нехорошее (получается сложное решение таких вопросов, как super() звонки и т. д.), B) если я добавлю новую таблицу, которую я должен запомнить, наследуть как от, так и от CommonRoutines и C) действительно, что Класс CommonRoutines - это «тип-тип» в некотором смысле. Действительно, CommonBase - это абстрактный базовый класс, который определяет набор полей &, которые являются общими для всех таблиц. Иными словами: «своя-а» абстрактная таблица.

Так, что я хотел бы это:

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 

Но это, конечно, не работает, как я тогда должен А) определить __tablename__ для AbstractTable, B) аспект ABC вещей вызывает всевозможные головные боли, а C) должен указывать какое-то отношение БД между AbstractTable и каждой отдельной таблицей.

Итак, мой вопрос: можно ли достичь этого разумным способом? В идеале я хотел бы, чтобы обеспечить соблюдение:

  • Нет множественного наследования
  • CommonBase/AbstractTable быть абстрактным (т.е. не может быть реализован)

ответ

14

Это довольно straigh вперед, вы просто сделать declarative_base() вернуться a Base класс, который наследует ваш CommonBase с использованием параметра cls=. Также показано в документах Augmenting The Base. Ваш код может выглядеть примерно следующим образом:

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

Поправьте меня, если я ошибаюсь, но эти «AbstractTable» уделы следует читать «Base», правильно? (например, 'class Table1 (Base):') –

+0

@AdamParkin: конечно. исправленный. – van

+0

Да, это именно то, что я искал. Благодаря! Единственным недостатком подхода является то, что теперь '' __init__' класса 'CommonBase' не вызван (' declarative_base' создает класс, который не вызывает 'super' в' '__init__', поэтому механизмы кооперативного множественного наследования Python не Работа). Хмм .... –

2

Вы можете использовать AbstractConcreteBase сделать 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

SQLAlchemy версию 0.7.3 ввела __abstract__ директивы, которая используется для абстрактных классов, не должны отображаться в таблице базы данных, хотя они являются подклассами sqlalchemy.ext.declarative.api.Base. Итак, теперь вы создать базовый класс, как это:

Base = declarative_base() 

class CommonRoutines(Base): 
    __abstract__ = True 

    id = Column(Integer, primary_key=True) 

    def __init__(self): 
     # ... 

Обратите внимание, как CommonRoutines не имеет атрибута __tablename__. Затем создайте подклассы, как это:

class Foo(CommonRoutines): 
    __tablename__ = 'foo' 

    name = Column(...) 

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

Это будет отображаться в таблице foo и наследует атрибут id от CommonRoutines.

Источник и дополнительная информация: http://docs.sqlalchemy.org/en/rel_0_7/orm/extensions/declarative.html#abstract

+0

Это то, что я действительно искал. благодаря – pylover