Sqlalchemy 如何实现一表多库?
在多区域情况下,每个区域都要一套完整的数据体系。然而管控层一般都是统一的,需要经常按照区域识别查询数据库。Sqlalchemy 提供了多库绑定功能,参考实现如下:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import MetaData
# 多个数据库连接配置信息
SQLALCHEMY_BINDS = {
'zbs': 'mysql://sharp:sharp@172.17.0.1:3306/zbs',
'sharp': 'mysql://sharp:sharp@172.17.0.1:3306/sharp',
}
app = Flask(__name__)
app.config['SQLALCHEMY_BINDS'] = SQLALCHEMY_BINDS
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
# 由于使用了 Flask,这里就直接一起搞了
db = SQLAlchemy(app)
class ZbsVolume(db.Model):
# 指定要连接的数据库
__bind_key__ = 'zbs'
# 指定对应的表名,默认是类名,这莫办法的事
__tablename__ = 'volume'
# 不然会爆表名冲突,无法初始化
metadata = MetaData()
id = db.Column(db.String(36), primary_key=True)
volume_type_name = db.Column(db.String(255))
class SharpVolume(db.Model):
__bind_key__ = 'sharp'
__tablename__ = 'volume'
metadata = MetaData()
id = db.Column(db.String(36), primary_key=True)
volume_type_name = db.Column(db.String(255))
定义好ORM,那使用就简单了,参考如下:
# 初始化数据库连接
db.create_all()
ZbsVolume.query.filter_by(id='fake').first()
# 新增一个
query = ZbsVolume(id=line, volume_type_name='hdd.std2)
db.session.add(query)
只有1个表两个库,这么写写也直观,如果要查 N 个表,又有 M 个库,这真成了无感情的机器人了。
动态生成 Model
这时候是不是想起来了类也是可以创建的,一个简单的元类不就行了,那我们就试试看
定义一个 meta.py 文件,用来存在原始表信息
from sqlalchemy import MetaData, Column, String, Integer
VolumeMeta = {
'__tablename__': 'volume',
'__table_args__': {'extend_existing': True},
'metadata': MetaData(),
'id': Column(String(36), primary_key=True),
'volume_type_name': Column(String(255)),
}
定义 model.py,用来创建 model
class ModelFactor(object):
def __init__(self):
self.cache = {}
self.regions = config.ZBS_MYSQL_MAP.keys()
self.meta_dict = {
'Volume': VolumeMeta,
'fake1": FakeMeta,
}
@classmethod
def make_model_key(cls, region, model):
return '%s_%s' % (region, model)
def create_model(self, region, model):
if model not in self.meta_dict:
logger.error("model <%s> not define" % model)
return None
if region not in self.regions:
logger.error("region <%s> not define" % region)
return None
db = init_db()
meta = self.meta_dict[model]
meta['__bind_key__'] = region
logger.info("Now create model <%s_%s>" % (region, model))
return type(model, (db.Model,), meta)
def select_model(self, region, model):
key = self.make_model_key(region, model)
if key not in self.cache:
model = self.create_model(region, model)
if model:
self.cache[key] = model
else:
model = self.cache[key]
return model
使用
model_factor = ModelFactor()
model = model_factor.select_model(region, 'Volume')
# 开始 orm 操作