【问题标题】:Sqlalchemy: avoiding multiple inheritance and having abstract base classSqlalchemy:避免多重继承并具有抽象基类
【发布时间】:2012-03-25 06:43:38
【问题描述】:

所以我有一堆使用 SQLAlchemy 的表,这些表被建模为从结果继承到对 declarative_base() 的调用的对象。即:

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

class Table2(Base):
     # __tablename__ & such here

等等。然后我想为我的每个 DB 表类提供一些通用功能,the easiest way to do this according to the docs 就是做多重继承:

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) 如果我添加一个新表,我必须记住从BaseCommonRoutines 继承,并且C)在某种意义上确实是“CommonRoutines”类“is-a”类型的表。真正的CommonBase 是一个抽象基类,它定义了一组所有表共有的字段和例程。换句话说:“its-a”抽象表。

所以,我想要的是这样的:

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) 为 AbstractTable 定义一个 __tablename__,B) 事物的 ABC 方面会导致各种头痛,C) 必须指出某种AbstractTable 和每个单独的表之间的数据库关系。

所以我的问题是:是否有可能以合理的方式实现这一目标?理想情况下,我想强制执行:

  • 没有多重继承
  • CommonBase/AbstractTable是抽象的(即不能实例化)

【问题讨论】:

    标签: python sqlalchemy multiple-inheritance


    【解决方案1】:

    如果你想拥有多个具有公共列的模型,那么你可以使用__abstract__@declared_attr来继承共享表属性。示例:

    Base = declarative_base()
    
    class CommonRoutines(Base):
        __abstract__ = True
    
        id = Column(Integer, primary_key=True)
        modified_at = Column(DateTime)
        
        @declared_attr
        def modified_by(self):
            # `user.id` is another table called `user` with an `id` field
            return Column(Integer, ForeignKey('user.id', name='fk_modified_by_user_id')) 
    
    
        def __init__(self):
            self.modified_by = None
            super().__init__()
    
    
    class Foo(CommonRoutines):
        __tablename__ = 'foo'
    
        name = Column(...)
    
    

    使用此解决方案,您将拥有一个 Foo 表,其中包含 Foo 类 (name) 和 CommonRoutines 中的字段 (idmodified_atmodified_by)

    【讨论】:

      【解决方案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 并从CommonRoutines 继承id 属性。

      来源及更多信息:http://docs.sqlalchemy.org/en/rel_0_7/orm/extensions/declarative.html#abstract

      【讨论】:

      • 它在我的情况下不起作用。 SqlAlchemy 在创建抽象类时将缺少__tablename__ 报告为错误
      【解决方案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
      

      【讨论】:

        【解决方案4】:

        这很简单,您只需使用 cls= 参数使 declarative_base() 返回一个从您的 CommonBase 继承的 Base 类。也显示在Augmenting The Base 文档中。您的代码可能如下所示:

        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
        

        【讨论】:

        • 如果我错了,请纠正我,但是那些“AbstractTable”继承应该是“Base”,对吗? (即class Table1(Base):
        • 是的,这正是我想要的。谢谢!该方法的唯一缺点是现在 CommonBase 类的 __init__ 不会被调用(declarative_base 生成的类不会在其 __init__ 中调用 super 所以 Python 的协作多重继承机制不会工作)。嗯……
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-10-02
        • 1970-01-01
        • 2014-08-26
        • 2014-08-16
        • 2016-11-05
        • 2023-03-04
        • 1970-01-01
        相关资源
        最近更新 更多