Openstack_SQLAlchemy_一对多关系表的多表插入实现
2016-09-25 16:34 云物互联 阅读(393) 评论(0) 收藏 举报目录
Openstack 与 SQLAlchemy
SQLAlchemy 是 Python 语言下的一款开源软件,它提供了 SQL 工具包以及对象关系映射器(ORM)。SQLAlchemy 主要分为两个部分:SQLAlchemy Core 和 SQLAlchemy ORM。
- 前者包括了 SQL 语言表达式/数据引擎/连接池等, 主要实现了:连接不同类型的数据库,提交查询和更新请求,定义数据库数据类型和定义 Schema 等功能。
- 后者提供了数据映射模式,将程序中的对象数据映射成数据库中的关系数据。但不可避免的会因为映射而降低程序效率。
SQLAlchemy 最大的好处在于,当数据从一中数据库管理器系统迁移到另外一种数据库管理系统时(EG. MySQL –> PostgreSQL),Python 程序员可以不用修改或者修改少量配置文件后仍能正常运行。所以 SQLAlchemy 是 Python 语言中较为常用的一款数据库 ORM 工具,也是 Openstack 默认支持 ORM 工具。
本篇主要记录了,我们如何对数据库中 一对多 的关系表进行多表插入。
一个多表插入的 Demo
在这个 Demo 中定义了 Table: virtual_machines 和 Table:datastores。这两个实体对象之间的关系为:一个 VirtualMachine 可以对应拥有多个 Datastore 。
- Step 1: 在数据库初始化模块中中定义 Table
virtual_machines和datastores
# octopunch/octopunch/db/sqlalchemy/migrate_repo/versions/001_octopunch_init.py
def define_tables(meta):
virtual_machines = Table(
'virtual_machines', meta,
Column('uuid', String(length=45), primary_key=True, nullable=False),
Column('vm_value', String(length=255)),
Column('name', String(length=255)),
mysql_engine='InnoDB'
)
datastores = Table(
'datastores', meta,
Column('uuid', String(length=45), primary_key=True, nullable=False),
Column('ds_value', String(length=255)),
Column('name', String(length=255)),
Column('virtual_machine_uuid', String(length=45),
ForeignKey('virtual_machines.uuid')),
mysql_engine='InnoDB'
)
NOTE: Table datastores 的外键值为 virtual_machines 的主键值
- Step 2: 在 SQLAlchemy 的 models 模块中定义对应的 class,并且将它们之间的关系设定为为一对多的关系
数据库二维表中的一行记录成为了一个类,一个字段成为了一个类属性
# octopunch/octopunch/db/sqlalchemy/models.py
class VirtualMachine(BASE, OctopunchBase):
"""Represents the virtual machine list."""
__tablename__ = 'virtual_machines'
uuid = Colmn(String(45), primary_key=True, primary_key=True)
vm_value = Column(String(255))
name = Column(String(255))
datastores = relationship('Datastore', backref='virtual_machines',
foreign_keys='Datastore.virtual_machine_uuid',
primaryjoin='VirtualMachine.uuid =='
'Datastore.virtual_machine_uuid')
class Datastore(BASE, OctopunchBase):
"""Represents the datastore list."""
__tablename__ = 'datastores'
uuid = Colmn(String(45), primary_key=True, primary_key=True)
ds_value = Column(String(45))
name = Column(String(255))
virtual_machine_uuid = Column(String(45),
ForeignKey('virtual_machines.uuid'))
NOTE:SQLAlchemy 提供的 relationship() 函数用于定义表与表之间的关系,通过参数 foreign_keys 和 backref 指定了 Table virtual_machines 和 datastores 之间的关系为 双向的一对多关系 。
详见:SQLAlchemy_定义(一对一/一对多/多对多)关系
- Step 3: 定义 SQLAlchemy 的数据库操作接口函数, 这里定义了对 Table
virtual_machines的 create 操作
NOTE:因为我们希望实现多表插入,即一次性插入两张表的数据,所以只要定义对一张表的 SQLAlchemy creae 函数即可。但这一函数必须是对应表示 ‘一’ 的一张表。
EG.Tablevirtual_machines:
# octopunch/octopunch/db/sqlalchemy/api.py
def _resource_refs(resource_dict, resource_class):
resource_ref = resource_class()
for k, v in resource_dict.items():
resource_ref[k] = v
return resource_ref
@require_context
def virtual_machine_create(context, values):
datastores = values.pop('datastores')
# 将键值对 'datastores': '...' 中的 Str(datastores) 类型对象替换为 models.Datastore(datastores) 对象
values['datastores'] = [_resource_refs(datastore, models.Datastore)
for datastore in datastores]
if not values.get('uuid'):
values['uuid'] = str(uuid.uuid4())
virtual_machine_ref = models.VirtualMachine()
virtual_machine_ref.update(values) # update 类 VirtualMachine 实例对象 virtual_machine_ref 的成员属性值
session = get_session()
with session.begin():
session.add(virtual_machine_ref)
return virtual_machine_ref
先看看,接口函数 virtual_machine_create(context, values) 中的形参 values 就是要写入到数据库中的数据。values 是一个 Dict 的数据类型对象。其 key 为数据库表的字段名, value 为字段对应的值。
一般来说,形参 values 的键值对元素和数据库表中的字段是相匹配的,个数也会相同。
但需要注意的是: 如果希望对 一对多 或 多对多 的关系表进行多表插入,那么就需要将表示为 ‘多’ 的一方的插入表数据也合并为存放于该 values 中一个键值对元素。
EXAMPLE: Table virtual_machines 的 values 格式
virtual_machine = {
'uuid': '<virtual_machine_uuid>',
'vm_value': '<virtual_machine_value>',
'name': '<virtual_machine_name>',
# 必须为 Table datastores 的 table_name
'datastores': '[
{
"name": "<datastore_1_name>",
...
},
{
"name": "<datastore_2_name>",
...
},
...
]'
}
为什么只需要按照这样的合适定义 values 就可以实现我们想要的结果呢?
是因为我们在 models 模块定义 class VirtualMachine 和 Datastore 时,指定了两者之间为 双向一对多 的关系,所以 SQLAlchemy 会在 VirtualMachine 中添加 datastores 成员属性。SQLAlchemy 会自动为该属性赋予上述字典中 virtual_machine['datastores'] 的值。
这个动作在语句 virtual_machine_ref.update(values) 处执行。
注意:请回过头在看看 Step 3 的代码实现。
- Step 4: 为
octopunch.octopunch.db.sqlalchemy.api.virtual_machine_create()传入 values 的实参
实际上你可以在任何地方,调用并为其传入实参octopunch.octopunch.db.sqlalchemy.api.,所以这里就不给出实例代码了。
virtual_machine_create()
可以参考: Openstack 通过 SQLAlchemy-ORM 访问数据库
小结
如何通过 SQLAlchemy 为 一对多 关系表进行多表插入?
- 需要在 models 模块中定义表的类,并指定表之间的关系
- 定义 SQLAlchemy 的操作接口函数时,只需要定义一张表对应的 create 函数即可
- create 函数的形参数 values 的格式遵守上述 EXAMPLE 的格式
- 在 create 函数中要实例化两张表对应在 models 模块中的类对象
本质上,该 Demo 就是对 SQLAlchemy 的一次 ORM 操作 —— 将对二维表的操作转化为对类对象操作的方式。
1. 实例化得到 models 模块中的表类对象。
2. 调用表类对象的 update 函数将需要写入到数据库中的数据更新到表类对象的成员属性值。
3. 创建连接数据库的 session 对象。
4. 调用 session.add() 将表类对象的成员属性值写入到数据库的二维表中。
浙公网安备 33010602011771号