Sqlalchemy: избегать множественного наследования и иметь абстрактный базовый класс
Итак, у меня есть куча таблиц с использованием SQLAlchemy, которые моделируются как объекты, которые наследуют результат до вызова declarative_base()
. То есть:
Base = declarative_base()
class Table1(Base):
# __tablename__ & such here
class Table2(Base):
# __tablename__ & such here
Etc. Затем я хотел иметь некоторые общие функциональные возможности для каждого из моих классов таблиц DB, самый простой способ сделать это в соответствии с документами - это просто сделать множественное наследование:
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), если я добавлю новую таблицу, которую я должен запомнить наследуют как от Base
, так и от CommonRoutines
и C), действительно, что класс CommonRoutines "is-a" тип таблицы в некотором смысле. Действительно, 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
Но это, конечно, не сработает, так как я тогда должен A) определить a __tablename__
для AbstractTable
, B) АБС-аспект вещей вызывает всевозможные головные боли, а C) должен указать какой-то вид отношения БД между AbstractTable
и каждой отдельной таблицей.
Итак, мой вопрос: можно ли достичь этого разумным образом? В идеале я хотел бы обеспечить соблюдение:
- Нет множественного наследования
-
CommonBase
/AbstractTable
быть абстрактным (т.е. не может быть создан)
Ответы
Ответ 1
Это довольно странно, вы просто заставляете declarative_base()
возвращать класс Base
, который наследует ваш CommonBase
с помощью параметра cls=
. Также показано в Дополнение базы. Тогда ваш код будет выглядеть примерно так:
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
Ответ 2
В 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
Ответ 3
Вы можете использовать AbstractConcreteBase для создания базовой модели абзаца:
from sqlalchemy.ext.declarative import AbstractConcreteBase
class AbstractTable(AbstractConcreteBase, Base):
id = db.Column(db.Integer, primary_key=True)
@classmethod
def somecommonaction(cls):
# body here