从抽象类异常继承继承SQLAlchemy类时:元类冲突:派生类的元类必须为

Bar*_*rka 4 python abstract-class sqlalchemy python-3.x

以下代码是带有一个简单表的SqlAlchemy ORM的非常简单的实现。Mytable类尝试从BaseAbstract继承。

该代码引发以下异常:

消息:元类冲突:派生类的元类必须是其所有基元元类的(非严格)子类

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)
Run Code Online (Sandbox Code Playgroud)

如果将类声明行更改为

类Mytable(Base):

该代码将正常工作。另外,如果您更改class BaseAbstract(ABC):class BaseAbstract(object):代码,也可以再次正常工作。如何从SQLAlchemy中的抽象类继承?

Ilj*_*ilä 5

混合元类并不容易,应避免使用。SQLAlchemy提供了一种处理抽象基类扩充基类的方法,另一方面,您尝试执行的操作看起来很像mixin

您可以使用以下命令指示SQLAlchemy跳过为类创建表和映射器__abstract__

Base = declarative_base()

class BaseAbstract(Base):
    """description of class"""
    __abstract__ = True

class Mytable(BaseAbstract):
    ...
Run Code Online (Sandbox Code Playgroud)

您也可以增加Base课程

class BaseAbstract:
    """description of class"""

Base = declarative_base(cls=BaseAbstract)

class Mytable(Base):
    ...
Run Code Online (Sandbox Code Playgroud)

但是在我看来,最简单的解决方案是完全放弃使用“抽象库”,并将其视为mixin,就像您已经做过的那样:

class CommonMixin:
    """description of class"""

Base = declarative_base()

class Mytable(CommonMixin, Base):
    ...
Run Code Online (Sandbox Code Playgroud)

但是,如果您坚持使用实际的abc.ABC抽象基类,请将模型类注册为虚拟子类:

class BaseAbstract(ABC):
    """description of class"""

Base = declarative_base()

@BaseAbstract.register
class Mytable(Base):
    ...
Run Code Online (Sandbox Code Playgroud)

缺点是@abc.abstractmethod在实例化虚拟子类时不检查修饰的方法。

如果上述不符合您的需求,您要使用ABC的检查所需的方法来实现,你可以尝试这样做的指示异常,并创建一个新的元类是组合DeclarativeMetaABCMeta

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>
Run Code Online (Sandbox Code Playgroud)

不过,我不能保证。它可能包含许多惊喜。