Sqlalchemy: избегать множественного наследования и иметь абстрактный базовый класс - PullRequest
15 голосов
/ 07 марта 2012

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

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

class Table2(Base):
     # __tablename__ & such here

Etc. Затем я хотел иметь некоторые общие функциональные возможности, доступные для каждого из моих классов таблиц БД, самый простой способ сделать это в соответствии с документами - это просто сделать множественное наследование:

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" в некотором смысле является таблицей типа. На самом деле 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) определить __tablename__ для AbstractTable, B) аспект ABC вещей вызывает все виды головных болей, и C) должен указывать на некоторый вид связи БД между AbstractTable и каждой отдельной таблицей.

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

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

Ответы [ 3 ]

30 голосов
/ 07 сентября 2013

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

15 голосов
/ 08 марта 2012

Это довольно просто, вы просто делаете 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
3 голосов
/ 07 июня 2012

Вы можете использовать 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...