有必要了解一下sqlalchemy的一些基本概念,当然了没有比官方文档更好的地方,官网的文档写的也算是简介明了,以其为主:
http://docs.sqlalchemy.org/en/rel_1_0/
如果你不是关注与openstack的数据库方面,比如百度的人搞openstack貌似想做这方面的东西,他们想优化openstack的数据库。
从网络上或者官网上可以看到sqlalchemy分为两部分:
SQLAlchemy Core #sql语言构造器,写起来和直接call sql语句很像
SQLAlchemy ORM #ORM功能,对象关系映射,这个是一个专门的方向,东西很多
当我们使用SQLAlchemy core时候,数据库作为一个一个的表而存在,每一条数据是一行,而在ORM中,数据成为类,而每一条数据是类的一个实例,
我们可以认为先有的core,而后在core的基础上实现的ORM。
上一篇说sqlalchemy封装了不同数据库的操作,在core中创建表(216_havana.py中取了一个例子):
meta = MetaData()
meta.bind = migrate_engine
agent_builds = Table('agent_builds', meta,
Column('created_at', DateTime),
Column('updated_at', DateTime),
Column('deleted_at', DateTime),
Column('id', Integer, primary_key=True, nullable=False),
Column('hypervisor', String(length=255)),
Column('os', String(length=255)),
Column('architecture', String(length=255)),
Column('version', String(length=255)),
Column('url', String(length=255)),
Column('md5hash', String(length=255)),
Column('deleted', Integer),
mysql_engine='InnoDB',
mysql_charset='utf8'
)
数据库中Table定义时,最终用的是metadata,你可以认为在metadata中指明了创建(操作)数据库表时使用的engine是什么。
使用agent_builds.create()即可创建database中的表,一旦表被创建,则可以如下使用Table:
table = Table('project_user_quotas', meta, autoload=True)
i=table.insert()
i.execute(xxxxxx) #执行插入动作
在nova/test/db的case中我们可以看到:
table.insert().execute(fake_quotas)
# Check we can get the longest resource name.
quota = table.select(table.c.id == 5).execute().first()
其中table.c访问table中的colums
在mark的link中有一段话对sqlalchemy的ORM作用说的比较明白:
”使用ORM,你可以将表格(和其他可以查询的对象)同Python联系起来,放入映射集(Mappers)当中。
然后你可以执行查询并返回 对象实例 列表,而不是结果集。 对象实例 也被联系到一个叫做 Session 的对象,
确保自动跟踪对象的改变,并可以使用 flush 立即保存结果。“
类将会和数据库中的Table关联起来,这时怎么做到的呢,如果自己实现,很容易想到这就是天然的“map”,将table和
class 做个mapping就可以了(可能需要双向的map存在),事实上,sqlalchemy中也是类似的,称之为mapper,官网的
教程有一篇叫“Declare a Mapping”说的就是这个。
>>> from sqlalchemy.ext.declarative import declarative_base
>>> Base = declarative_base()
>>> from sqlalchemy import Column, Integer, String
>>> class User(Base):
... __tablename__ = 'users'
...
... id = Column(Integer, primary_key=True)
... name = Column(String)
... fullname = Column(String)
... password = Column(String)
...
... def __repr__(self):
... return "<User(name='%s', fullname='%s', password='%s')>" % (
... self.name, self.fullname, self.password)
这已经和openstack中的非常相似了,我们在nova/db/models.py中随便看一个定义的Model:
class Service(BASE, NovaBase):#此处Base便是declarative_base()
"""Represents a running service on a host."""
__tablename__ = 'services' #指明table名
__table_args__ = (
schema.UniqueConstraint("host", "topic", "deleted",
name="uniq_services0host0topic0deleted"),
schema.UniqueConstraint("host", "binary", "deleted",
name="uniq_services0host0binary0deleted") #唯一性限制,很容易理解,host,binary和deleted三个属性组成的字段具有唯一性
)
id = Column(Integer, primary_key=True)
host = Column(String(255)) # , ForeignKey('hosts.id'))
binary = Column(String(255))
topic = Column(String(255))
report_count = Column(Integer, nullable=False, default=0)
disabled = Column(Boolean, default=False)
disabled_reason = Column(String(255))
#至于Index之类的都很好理解,不再赘述,有一些数据库基础的理解起来都不难
另一个父类NovaBase,其中主要实现了save方法,
def save(self, session=None):
from nova.db.sqlalchemy import api
if session is None:
session = api.get_session()
super(NovaBase, self).save(session=session)
这也就是我们在nova/db/api.py中看到有些操作中:
service_ref = models.Service()
service_ref.update(values)
service_ref.save()
类似的操作save是如何工作的,就是因为NovaBase的原因。
在数据库操作的部分,我们常能看到“session”,session是什么呢?
“
所有对映射的操作都需要一个重要的对象叫做 Session 。所有对象通过映射的载入和保存都 必须 通过 Session 对象,有如对象的工作空间一样被加载到内存。
特定对象在特定时间只能关联到一个 Session 。
”
我理解成为,要想通过类操作变成为数据库操作,需要通过映射,同时需要通过session,像是真正操作数据库的连接。
session的另一个重要特性是其事务性的保证,
transaction=session.create_transaction()
transaction.commit()
transaction.rollback()
可以用于事务性的控制,在neutron中,只有网络命名执行成功,数据库的变化才会commit,就是这个用法。
sqlalchemy的东西本身是比较深比较多的,话说回来,数据库就不简单,对于sqlalchemy core在migrate_repo中使用较多,由于涉及到对表的创建,
更改等,sqlalchemy orm的使用,nova/db/api.py中使用较多,实际的openstack大部分操作最终都会到db中定义的操作来,而这时基本是ORM的使用
场景。
mark link这一篇讲的不错
http://blog.youkuaiyun.com/dupei/article/details/6014488