在SQLAlchemy中动态设置__tablename__进行分片?

Sum*_*man 14 python mysql orm sqlalchemy

为了处理不断增长的数据库表,我们在表名上进行分片.所以我们可以拥有这样命名的数据库表:

table_md5one
table_md5two
table_md5three
Run Code Online (Sandbox Code Playgroud)

所有表都具有完全相同的模式.

我们如何使用SQLAlchemy并动态指定与此对应的类的表名?看起来declarative_base()类需要预先指定tablename.

最终将有太多表来从父/基类手动指定派生类.我们希望能够构建一个可以动态设置表名的类(可以作为参数传递给函数.)

Sum*_*man 14

好的,我们使用自定义SQLAlchemy声明而不是声明性声明.

所以我们创建一个动态表对象,如下所示:

from sqlalchemy import MetaData, Table, Column

def get_table_object(self, md5hash):
    metadata = MetaData()
    table_name = 'table_' + md5hash
    table_object = Table(table_name, metadata,
        Column('Column1', DATE, nullable=False),
        Column('Column2', DATE, nullable=False)
    )
    clear_mappers()
    mapper(ActualTableObject, table_object)
    return ActualTableObject
Run Code Online (Sandbox Code Playgroud)

其中ActualTableObject是映射到表的类.


jav*_*vex 8

扩充基础中,您会找到一种使用自定义Base类的方法,例如,可以__tablename__动态计算属性:

class Base(object):
    @declared_attr
    def __tablename__(cls):
        return cls.__name__.lower()
Run Code Online (Sandbox Code Playgroud)

这里唯一的问题是我不知道你的哈希来自哪里,但这应该是一个很好的起点.

如果您不是要求所有表格使用此算法,而只需要一个declared_attr表格,则可以使用您对分片感兴趣的表格.


kir*_*pit 8

因为我坚持使用__tablename__由给定参数动态指定的声明性类,在使用其他解决方案失败数天并研究 SQLAlchemy 内部结构后,我想出了以下我认为简单、优雅且无竞争条件的解决方案。

def get_model(suffix):
    DynamicBase = declarative_base(class_registry=dict())

    class MyModel(DynamicBase):
        __tablename__ = 'table_{suffix}'.format(suffix=suffix)

        id = Column(Integer, primary_key=True)
        name = Column(String)
        ...

    return MyModel
Run Code Online (Sandbox Code Playgroud)

由于他们有自己的class_registry,您不会收到警告说:

这个声明性基已经包含一个与 mypackage.models.MyModel 具有相同类名和模块名的类,并将在字符串查找表中替换。

因此,您将无法通过字符串查找从其他模型中引用它们。但是,将这些即时声明的模型用于外键也可以很好地工作:

ParentModel1 = get_model(123)
ParentModel2 = get_model(456)

class MyChildModel(BaseModel):
    __tablename__ = 'table_child'

    id = Column(Integer, primary_key=True)
    name = Column(String)
    parent_1_id = Column(Integer, ForeignKey(ParentModel1.id))
    parent_2_id = Column(Integer, ForeignKey(ParentModel2.id))
    parent_1 = relationship(ParentModel1)
    parent_2 = relationship(ParentModel2)
Run Code Online (Sandbox Code Playgroud)

如果您仅使用它们来查询/插入/更新/删除而没有任何引用(例如来自另一个表的外键引用),则它们、它们的基类以及它们的 class_registry 将被垃圾收集,因此不会留下任何痕迹。


Dvi*_*rad 7

您可以编写一个带有 tablename 参数的函数,并通过设置适当的属性发回该类。

def get_class(table_name):

   class GenericTable(Base):

       __tablename__ = table_name

       ID= Column(types.Integer, primary_key=True)
       def funcation(self):
        ......
   return GenericTable
Run Code Online (Sandbox Code Playgroud)

然后您可以使用以下方法创建表:

get_class("test").__table__.create(bind=engine)  # See sqlachemy.engine
Run Code Online (Sandbox Code Playgroud)