The following code is a very simple implementation of a SqlAlchemy ORM with one simple table. The Mytable class tries to inherit from BaseAbstract.
The code throws the following exception:
Message: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
from abc import ABC
from sqlalchemy import Column, Integer, create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
class BaseAbstract(ABC):
"""description of class"""
SQLALCHEMY_DATABASE_URI =\
'mssql+pyodbc://(local)/TestDB?driver=SQL+Server+Native+Client+11.0'
SQLALCHEMY_TRACK_MODIFICATIONS = False
engine = create_engine(SQLALCHEMY_DATABASE_URI, echo=True)
Session = sessionmaker(bind=engine)
session = Session()
Base = declarative_base()
metadata = Base.metadata
class Mytable(Base, BaseAbstract):
__tablename__ = 'myTable'
id = Column(Integer, primary_key=True)
firstNum = Column(Integer, nullable=False)
secondNum = Column(Integer, nullable=False)
If you change the class declaration line to
class Mytable(Base):
the code will work fine. Also if you change class BaseAbstract(ABC):
to class BaseAbstract(object):
the code will again work fine.
How do I inherit from an abstract class in SQLAlchemy?
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With