When inheriting SQLAlchemy class from abstract class exception thrown: metaclass conflict: the metaclass of a derived class must be
Mixing metaclasses is not easy and you should avoid it. SQLAlchemy offers a way to handle abstract base classes or augmenting the base, and on the other hand what you're trying to do looks a lot like a mixin.
You can instruct SQLAlchemy to skip creating a table and a mapper for a class using __abstract__
:
Base = declarative_base()
class BaseAbstract(Base):
"""description of class"""
__abstract__ = True
class Mytable(BaseAbstract):
...
You could also augment the Base
class:
class BaseAbstract:
"""description of class"""
Base = declarative_base(cls=BaseAbstract)
class Mytable(Base):
...
But in my opinion the easiest solution is to forego using an "abstract base" altogether and think of it as a mixin, as you had done already in a way:
class CommonMixin:
"""description of class"""
Base = declarative_base()
class Mytable(CommonMixin, Base):
...
But if you insist on using an actual abc.ABC
abstract base class, register your model classes as virtual subclasses:
class BaseAbstract(ABC):
"""description of class"""
Base = declarative_base()
@BaseAbstract.register
class Mytable(Base):
...
The downside is that @abc.abstractmethod
decorated methods are not checked upon instantiating virtual subclasses.
If the above do not fulfill your needs and you want to use ABC
for checking that required methods are implemented, you could try and do as the exception instructed and create a new metaclass that is the combination of DeclarativeMeta
and ABCMeta
:
In [6]: class DeclarativeABCMeta(DeclarativeMeta, abc.ABCMeta):
...: pass
...:
In [7]: Base = declarative_base(metaclass=DeclarativeABCMeta)
In [8]: class BaseAbstract(abc.ABC):
...: @abc.abstractmethod
...: def foo(self):
...: pass
...:
In [13]: class MyTable(Base, BaseAbstract):
...: __tablename__ = 'mytable'
...: id = Column(Integer, primary_key=True)
...:
In [14]: MyTable()
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-14-1686a36a17c6> in <module>()
----> 1 MyTable()
TypeError: "Can't instantiate abstract class MyTable with abstract methods foo"
In [18]: class MyOtherTable(Base, BaseAbstract):
...: __tablename__ = 'myothertable'
...: id = Column(Integer, primary_key=True)
...: def foo(self):
...: return 'bar'
...:
In [19]: MyOtherTable()
Out[19]: <__main__.MyOtherTable at 0x7f01b4b592b0>
I cannot vouch for this, though. It might contain more than a few surprises.