Подтвердить что ты не робот

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 быть абстрактным (т.е. не может быть создан)
4b9b3361

Ответ 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