原文:https://docs.sqlalchemy.org/en/13/orm/extensions/declarative/mixins.html
Superset/models/core.py里面,Database的定义:
class Database(Model, AuditMixinNullable, ImportMixin):
"""An ORM object that stores Database related information"""
Mixin是SQLAlchemy的一个概念,给我的感觉,有点像C++的Helper继承。在业务逻辑之外的一些辅助功能,会做到Helper类里面,然后在class树的顶层,就把Helper当成基类的多重继承来源。
Helper和一般C++入门的书里面讲的多重继承的不同在于,Helper的定位是辅助工具,和“主”基类互补。“主”基类定义概念用的(用抽象类声明一组接口),Helper类共享函数实现用的。
以下为官方文档的翻译,不当之处,敬请指正:
SQLAlchemy的Declarative体系的,通常被用于在一些类之间共享某些功能。例如一组公共列(column),一些公共表选项或其他映射属性。Python的惯用法,是把这些公共功能归集的某个基类当中,再由派生类去继承。
Declarative体系的惯用法,是允许自定义的“主”基类加入到Declarative体系中来,同时,还允许“主”基类之外的“Mixin”类加入到这个体系。Declarative体系包含几项辅助功能,可以根据声明的映射方式进行操作。一些常见的混合(mixed-in)惯用法的例子如下:
from sqlalchemy.ext.declarative import declared_attr
class MyMixin(object):
@declared_attr
def __tablename__(cls):
return cls.__name__.lower()
__table_args__ = {'mysql_engine': 'InnoDB'}
__mapper_args__= {'always_refresh': True}
id = Column(Integer, primary_key=True)
class MyModel(MyMixin, Base):
name = Column(String(1000))
上述MyModel类,会包含一个名为“id”的列作为主键,一个名为__tablename__的属性,该属性派生自类本身的名称,以及由Mixin类MyMixin定义的__table_args__、__mapper_args__。
MyMixin该放在Base之前还是之后,没有固定的约定。适用正常的Python方法解析规则,上面的示例写成这样也可以:
class MyModel(Base, MyMixin):
name = Column(String(1000))
这样能行得通,是因为Base和MyMixin的属性之间没有交集。也就是,__tablename__、__table_args__、 id等成员都是由MyMixin定义的。如果Base中也定义了同名变量,就要看Base和MyMixin在继承列表中哪个在前,派生类中的继承变量,会来自于在前的那一个基类。
扩展Base
除了使用纯mixin之外,本节中的大多数技术也可以应用于Base本身,适用于应该应用于从特定基类派生的所有类的模式。 这是使用declarative_base()函数的cls参数实现的:
from sqlalchemy.ext.declarative import declared_attr
class Base(object):
@declared_attr
def __tablename__(cls):
return cls.__name__.lower()
__table_args__ = {'mysql_engine': 'InnoDB'}
id = Column(Integer, primary_key=True)
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base(cls=Base)
class MyModel(Base):
name = Column(String(1000))
在上面,MyModel和从Base派生的所有其他类将具有从类名派生的表名,id主键列,以及MySQL的“InnoDB”引擎。
在列中混合
在mixin上指定列的最基本方法是通过简单声明:
class TimestampMixin(object):
created_at = Column(DateTime, default=func.now())
class MyModel(TimestampMixin, Base):
__tablename__ = 'test'
id = Column(Integer, primary_key=True)
name = Column(String(1000))
关系中的Mixin
在SQLAlchemy的declarative体系里面,Mixin惯用法也可以用在relationship中。relationship()函数创建的关系,可以用declared_attr方法,消除在复制关系及其绑定字段时可能产生的歧义。如下的示例,将外键和relationship组合在一起,这样Foo和Bar两个派生类,都可以被配置为通过多对一的关系关联到Target类(译注:目前正在做单独使用Superset后端的定制开发,会新增很多张表,似乎本节的方法可以用在模型定义上)。
class RefTargetMixin(object):
@declared_attr
def target_id(cls):
return Column('target_id', ForeignKey('target.id'))
@declared_attr
def target(cls):
return relationship("Target")
class Foo(RefTargetMixin, Base):
__tablename__ = 'foo'
id = Column(Integer, primary_key=True)
class Bar(RefTargetMixin, Base):
__tablename__ = 'bar'
id = Column(Integer, primary_key=True)
class Target(Base):
__tablename__ = 'target'
id = Column(Integer, primary_key=True)
(待更新...)