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

时间:2013-10-03 16:05:13

标签: python mysql orm sqlalchemy

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

table_md5one
table_md5two
table_md5three

所有表都具有完全相同的架构。

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

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

6 个答案:

答案 0 :(得分:13)

好的,我们使用了自定义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

其中ActualTableObject是映射到表的类。

答案 1 :(得分:8)

Augmenting the Base中,您可以找到一种使用自定义Base类的方法,例如,可以动态计算__tablename__ attribure:

class Base(object):
    @declared_attr
    def __tablename__(cls):
        return cls.__name__.lower()

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

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

答案 2 :(得分:2)

试试这个

import zlib

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, BigInteger, DateTime, String

from datetime import datetime

BASE = declarative_base()
ENTITY_CLASS_DICT = {}


class AbsShardingClass(BASE):

    __abstract__ = True

def get_class_name_and_table_name(hashid):
    return 'ShardingClass%s' % hashid, 'sharding_class_%s' % hashid

def get_sharding_entity_class(hashid):
    """
    @param hashid: hashid
    @type hashid: int
    @rtype AbsClientUserAuth
    """

    if hashid not in ENTITY_CLASS_DICT:
        class_name, table_name = get_class_name_and_table_name(hashid)
        cls = type(class_name, (AbsShardingClass,),
                   {'__tablename__': table_name})
        ENTITY_CLASS_DICT[hashid] = cls

    return ENTITY_CLASS_DICT[hashid]

cls = get_sharding_entity_class(1)
print session.query(cls).get(100)

答案 3 :(得分:1)

你可以用tablename参数编写一个函数,并通过设置适当的属性发送回类。

setw()

然后你可以使用:

创建一个表
def get_class(table_name):

   class GenericTable(Base):

       __tablename__ = table_name

       ID= Column(types.Integer, primary_key=True)
       def funcation(self):
        ......
   return GenericTable

答案 4 :(得分:1)

因为我坚持使用由给定参数动态指定的__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

由于他们拥有自己的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)

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

答案 5 :(得分:0)

您可以使用常规的declarative_base而不是使用命令式创建Table对象,并通过闭包将表名设置为以下内容:

def make_class(Base, table_name):
    class User(Base):
        __tablename__ = table_name
        id = Column(Integer, primary_key=True)
        name= Column(String)

    return User

Base = declarative_base()
engine = make_engine()
custom_named_usertable = make_class(Base, 'custom_name')
Base.metadata.create_all(engine)

session = make_session(engine)
new_user = custom_named_usertable(name='Adam')
session.add(new_user)
session.commit()
session.close()
engine.dispose()