編者按:《通過demo學(xué)習(xí)OpenStack開發(fā)》專欄是劉陳泓的系列文章,,專欄通過開發(fā)一個(gè)demo的形式來介紹一些參與OpenStack項(xiàng)目開發(fā)的必要的基礎(chǔ)知識(shí),,希望幫助大家入門企業(yè)級(jí)Python項(xiàng)目的開發(fā)和OpenStack項(xiàng)目的開發(fā)。劉陳泓主要關(guān)注OpenStack的身份認(rèn)證和計(jì)費(fèi)領(lǐng)域,。另外,,還對(duì)云計(jì)算、分布式系統(tǒng)應(yīng)用和開發(fā)感興趣,。 OpenStack中的關(guān)系型數(shù)據(jù)庫應(yīng)用OpenStack中的數(shù)據(jù)庫應(yīng)用主要是關(guān)系型數(shù)據(jù)庫,,主要使用的是MySQL數(shù)據(jù)庫。當(dāng)然也有一些NoSQL的應(yīng)用,,比如Ceilometer項(xiàng)目,。就SQL數(shù)據(jù)庫本身的應(yīng)用而言,OpenStack的項(xiàng)目和其他項(xiàng)目并沒有什么區(qū)別,,也是采用ORM技術(shù)對(duì)數(shù)據(jù)進(jìn)行增刪改查而已,。 本文的重點(diǎn)是講解OpenStack項(xiàng)目中對(duì)關(guān)系型數(shù)據(jù)庫的應(yīng)用的基礎(chǔ)知識(shí),更多的是涉及ORM庫的使用,。對(duì)于數(shù)據(jù)庫的安裝和配置,,需要讀者自己查找一下MySQL的教程,如果只是為了驗(yàn)證ORM的相關(guān)知識(shí),,也可以使用sqlite數(shù)據(jù)庫,。 數(shù)據(jù)庫的選擇OpenStack官方推薦的保存生產(chǎn)數(shù)據(jù)的是MySQL數(shù)據(jù)庫,在devstack項(xiàng)目(這個(gè)項(xiàng)目用于快速搭建OpenStack開發(fā)環(huán)境)中也是安裝了MySQL數(shù)據(jù)庫,。不過,,因?yàn)镺penStack的項(xiàng)目中沒有使用特定的只有在MySQL上才能用的功能,,而且所采用的ORM庫SQLAlchemy也支持多種數(shù)據(jù)庫,所以理論上選擇PostgreSQL之類的數(shù)據(jù)庫來替代MySQL也是可行的,。 另外,,OpenStack項(xiàng)目在單元測試中使用的是sqlite的內(nèi)存數(shù)據(jù)庫,這樣開發(fā)者運(yùn)行單元測試的時(shí)候不需要安裝和配置復(fù)雜的MySQL數(shù)據(jù)庫,,只要安裝好sqlite3就可以了,。而且,數(shù)據(jù)庫是保存在內(nèi)存中的,,會(huì)提高單元測試的速度,。 ORM的選擇什么是ORMORM的全稱是Object-Relational Mapping,即對(duì)象關(guān)系映射,,是一種利用編程語言的對(duì)象來表示關(guān)系數(shù)據(jù)庫中的數(shù)據(jù)的技術(shù),,其更形式化的定義可以參考Wiki頁面Orject-relational mapping。簡單的說,,ORM就是把數(shù)據(jù)庫的一張表和編程語言中的一個(gè)對(duì)象對(duì)應(yīng)起來,,這樣我們?cè)诰幊陶Z言中操作一個(gè)對(duì)象的時(shí)候,實(shí)際上就是在操作這張表,,ORM(一般是一個(gè)庫)負(fù)責(zé)把我們對(duì)一個(gè)對(duì)象的操作轉(zhuǎn)換成對(duì)數(shù)據(jù)庫的操作,。 Python中的ORM實(shí)現(xiàn)一般來說,各種主流語言都有自己的ORM實(shí)現(xiàn),,一般來說也不只一種,,比較出名的有Java的Hibernate,Ruby on Rails的ORM,,C++的ODB等,。在Python中也存在多種ORM的實(shí)現(xiàn),最著名的兩種是Django的Model層的ORM實(shí)現(xiàn),,以及SQLAlchemy庫,。這兩種ORM實(shí)現(xiàn)基本上是Python中ORM的事實(shí)上的標(biāo)準(zhǔn),如果你寫Django應(yīng)用,,那么你就用Django自帶的實(shí)現(xiàn),;不然,你就可以選擇SQLAlchemy庫,。 OpenStack基本上都是Python項(xiàng)目,,所以在OpenStack中,ORM主要是使用了SQLAlchemy庫(Keystone,、Nova、Neutron等),;不過使用了Django的Horizon項(xiàng)目(面板)還是使用了Django自帶的ORM實(shí)現(xiàn),。本文主要是講解OpenStack中如何使用SQLAlchemy庫,,這個(gè)也是開發(fā)OpenStack項(xiàng)目的最基本知識(shí)。 SQLAlchemySQLAlchemy簡介SQLAlchemy項(xiàng)目是Python中最著名的ORM實(shí)現(xiàn),,不僅在Python項(xiàng)目中也得到了廣泛的應(yīng)用,,而且對(duì)其他語言的ORM有很大的影響。OpenStack一開始選擇這個(gè)庫,,也是看中了它足夠穩(wěn)定,、足夠強(qiáng)大的特點(diǎn)。 SQLAlchemy項(xiàng)目最新的版本是1.0.11,,1.0系列是今年剛發(fā)的,,0.9系列應(yīng)該還是應(yīng)用最廣泛的版本。對(duì)于一般的應(yīng)用來說,,0.9系列和1.0系列差別不大,。 關(guān)于SQLAlchemy的學(xué)習(xí)我個(gè)人覺得SQLAlchemy的學(xué)習(xí)難度會(huì)比Django的Model層難一些,因?yàn)橐粋€(gè)最簡單的例子也會(huì)有一些不太直觀的地方,,對(duì)于沒用過的人來說,,會(huì)比較難以理解。不過SQLAlchemy官網(wǎng)整理了一些比較不錯(cuò)的入門教程,,是一個(gè)比較好的學(xué)習(xí)起點(diǎn):Tutorials,。另外,官方的Reference其實(shí)是一個(gè)很好的教程,,講了很多基本的概念,,有助于理解SQLAlchemy的庫的使用。Reference還提供PDF版本的下載,。我個(gè)人建議大家直接閱讀Reference即可,,閱讀順序就按照PDF文件的章節(jié)編排順序進(jìn)行。雖然這個(gè)文檔很長,,但是我最后發(fā)現(xiàn)這么做是最節(jié)約時(shí)間的,。 SQLAlchemy的架構(gòu)先讓我們來看一下SQLAlchemy這個(gè)庫的總體架構(gòu),如下圖(圖來自官網(wǎng))所示: SQLAlchemy這個(gè)庫分為兩層:
上面簡單的總結(jié)了SQLAlchemy的架構(gòu),,希望大家能夠大概了解一下SQLAlchemy,,在后面介紹一些相關(guān)概念時(shí),能夠知道這個(gè)概念是屬于整個(gè)架構(gòu)的哪個(gè)部分,。 Dialect和數(shù)據(jù)庫客戶端上面提到了Dialect是用來對(duì)接不同的數(shù)據(jù)庫驅(qū)動(dòng)的,,它主要負(fù)責(zé)將SQLAlchemy最后生成的數(shù)據(jù)庫操作轉(zhuǎn)換成對(duì)數(shù)據(jù)庫驅(qū)動(dòng)的調(diào)用,其中會(huì)處理一些不同數(shù)據(jù)庫和不同DBAPI實(shí)現(xiàn)的差別,。這個(gè)部分一般是SQLAlchemy的開發(fā)者關(guān)心的內(nèi)容,,如果你只是使用SQLAlchemy來操作數(shù)據(jù)庫,那么可以不用關(guān)心這個(gè)部分,。不過我們還是要來了解一下SQLAlchemy支持的和OpenStack相關(guān)的數(shù)據(jù)庫驅(qū)動(dòng),。 MySQL OpenStack項(xiàng)目主要是使用MySQL,之前一直都在使用MySQL-Python驅(qū)動(dòng),,因?yàn)檫@個(gè)驅(qū)動(dòng)足夠成熟和穩(wěn)定,。不過這個(gè)情況正在轉(zhuǎn)變,有如下兩個(gè)原因:
為了解決這個(gè)問題,,社區(qū)發(fā)起了一次對(duì)新驅(qū)動(dòng)的評(píng)估,,主要是評(píng)估MySQL-Python驅(qū)動(dòng):PyMySQL Evaluation。這個(gè)評(píng)估還在社區(qū)的郵件列表發(fā)起了好幾次討論,,到目前為止的結(jié)果是:如果使用Python 2.7,,那么繼續(xù)使用MySQL-Python這個(gè)驅(qū)動(dòng),否則就使用PyMySQL這個(gè)驅(qū)動(dòng),。PyMySQL驅(qū)動(dòng)是使用純Python寫的,,不僅支持Python3而且可以支持eventlet的異步。 SQLite3 OpenStack項(xiàng)目一般會(huì)使用SQLite3數(shù)據(jù)庫來運(yùn)行單元測試,。OpenStack在Python2.7下會(huì)使用pysqlite驅(qū)動(dòng),,不過這個(gè)驅(qū)動(dòng)和標(biāo)準(zhǔn)庫中的sqlite3模塊是一樣的,也就是Python內(nèi)置了SQLite3的驅(qū)動(dòng),,你無需選擇其他的驅(qū)動(dòng),。 SQLAlchemy的基本概念和使用使用SQLAlchemy大體上分為三個(gè)步驟:連接到數(shù)據(jù)庫,定義數(shù)據(jù)模型,,執(zhí)行數(shù)據(jù)操作,。 連接到數(shù)據(jù)庫在你的應(yīng)用可以使用數(shù)據(jù)庫前,你要先定義好數(shù)據(jù)庫的連接,,包括數(shù)據(jù)庫在哪里,,用什么賬號(hào)訪問等。所有的這些工作都是通過Engine對(duì)象來進(jìn)行的(記得上面提到的Engine了么,?),。 數(shù)據(jù)庫URL SQLAlchemy使用URL的方式來指定要訪問的數(shù)據(jù)庫,整個(gè)URL的具體格式如下: dialect+driver://username:password@host:port/database 其中,,dialect就是指DBMS的名稱,,一般可選的值有:postgresql、 mysql,、sqlite等,。driver就是指驅(qū)動(dòng)的名稱,如果不指定,,SQLAlchemy會(huì)使用默認(rèn)值,。database就是指DBMS中的一個(gè)數(shù)據(jù)庫,一般是指通過 創(chuàng)建Engine對(duì)象 確定了要連接的數(shù)據(jù)庫信息后,,就可以通過 from sqlalchemy import create_engineengine = create_engine('sqlite://:memory:')
還有很多其他的參數(shù),可以參考官方文檔:Engine Configuration,。 一般來說,,Engine對(duì)象會(huì)默認(rèn)啟用連接池,會(huì)根據(jù)不同的dialect來選擇不同的默認(rèn)值,。一般來說,,你是不用考慮連接池的配置的,默認(rèn)情況都配置好了,。想了解關(guān)于連接池的更多內(nèi)容,,請(qǐng)查看官方文檔:Connection Pooling。 使用Engine對(duì)象 一般來說,,應(yīng)用程序的代碼是不直接使用Engine對(duì)象的,,而是把Engine對(duì)象交給ORM去使用,或者創(chuàng)建session對(duì)象來使用,。不過,,我們還是來簡單看一下Engine對(duì)象能做什么事情。 應(yīng)用程序可以調(diào)用Engine對(duì)象的 定義數(shù)據(jù)模型有了數(shù)據(jù)庫連接后,,我們就可以來定義數(shù)據(jù)模型了,也就是定義映射數(shù)據(jù)庫表的Python類。在SQLAlchemy中,,這是通過Declarative的系統(tǒng)來完成的,。 Declarative系統(tǒng) 根據(jù)官方文檔的描述,SQLAlchemy一開始是采用下面這種方式來定義ORM的:
上面這種方式稱為Classical Mappings,看起來好麻煩啊,。所以就有了Declarative系統(tǒng),。這個(gè)系統(tǒng)就是一次完成這三個(gè)步驟,你只需要定義步驟1中的類即可,。這也是現(xiàn)在在SQLAlchemy中使用ORM的方式,,無需在使用過去這種麻煩的方法。 要使用Declarative系統(tǒng),,你需要為所有映射類創(chuàng)建一個(gè)基類,,這個(gè)基類用來維護(hù)所有映射類的元信息。 from sqlalchemy.ext.declarative import declarative_baseBase = declarative_base() 定義映射類 現(xiàn)在我們可以開始創(chuàng)建映射類了,。假設(shè)我們?cè)跀?shù)據(jù)庫中有一個(gè)表Person,,這個(gè)表有兩個(gè)列,分別是id和name,,那么我們創(chuàng)建的映射類如下: from sqlalchemy import Column, Integer, String# 這里的基類Base是上面我們通過declarative_base函數(shù)生成的class Person(Base): __tablename__ = 'person' id = Column(Interger, primary_key=True) name = Column(String(250), nullable=False) 這樣我們就定義了一個(gè)映射類Person,,后續(xù)我們可以通過操作這個(gè)類的實(shí)例來實(shí)現(xiàn)對(duì)數(shù)據(jù)庫表person的操作。在我們的映射類中,,我們使用 因?yàn)槲覀兪褂昧薉eclarative系統(tǒng),,所以雖然我們自己沒有定義Table對(duì)象,,但是Declarative系統(tǒng)幫我們做了,并且?guī)臀覀冋{(diào)用了 In [6]: Person.__table__Out[6]: Table('person', MetaData(bind=None), Column('id', Integer(), table= 定義映射類是我們使用ORM的最主要的功能之一,不僅可以指定單表的映射,,還能夠指定表之間的關(guān)系,。由于篇幅限制,我們?cè)诒疚木筒徽归_講了,。 Schema和Metadata關(guān)于Table對(duì)象,,我們上面也提到了,,它屬于SQLAlchemy的core層的Schema/Types這個(gè)部分,。SQLAlchemy中的Schema可以理解為和DDL相關(guān)的一套體系,,它告訴SQLAlchemy的其他部分,,數(shù)據(jù)庫中的表是如何定義的,。這個(gè)相當(dāng)于我們?cè)贛ySQL中使用 SQLAlchemy中通過schema metadata來實(shí)現(xiàn)上面說的Schema,。Schema metadata,,官方文檔中也稱為database metadata,,簡稱為metadata,是一個(gè)容器,,其中包含了和DDL相關(guān)的所有信息,,包括Table、Column等對(duì)象,。當(dāng)SQLAlchemy要根據(jù)映射類生成SQL語句時(shí),,它會(huì)查詢metadata中的信息,根據(jù)信息來生成SQL語句,。 為了要讓metadata可以工作,,我們需要把DDL的相關(guān)信息放到metadata中。如果你注意看上面 好在Declarative系統(tǒng)則幫我們把這些都做好了。當(dāng)我們通過 說了這么多關(guān)于metadata的內(nèi)容,,簡單總結(jié)一下:metadata是schema在SQLAlchemy中的實(shí)現(xiàn),包含了DDL的信息,,SQLAlchemy中的其他部分需要依賴于metadata中的信息,,一般用戶很少使用metadata。 很少用,?那說這么多是做啥,?主要是讓讀者可以理解下面這個(gè)語句的原理: Base = declarative_base()# 基于Base定義映射類Base.metadata.create_all(engine) 最后這行代碼是我們最常用到metadata的地方:創(chuàng)建所有的表。我們告訴 from sqlalchemy import Column, Integer, Stringfrom sqlalchemy.ext.declarative import declarative_basefrom sqlalchemy import create_engineBase = declarative_base()class Person(Base): __tablename__ = 'person' id = Column(Integer, primary_key=True) name = Column(String(250), nullable=False)engine = create_engine('sqlite:///:memory:', echo=True)Base.metadata.create_all(engine) 輸出結(jié)果如下: ...2016-01-06 09:56:03,600 INFO sqlalchemy.engine.base.Engine PRAGMA table_info('person')2016-01-06 09:56:03,601 INFO sqlalchemy.engine.base.Engine ()2016-01-06 09:56:03,602 INFO sqlalchemy.engine.base.EngineCREATE TABLE person ( id INTEGER NOT NULL, name VARCHAR(250) NOT NULL, PRIMARY KEY (id))2016-01-06 09:56:03,603 INFO sqlalchemy.engine.base.Engine ()2016-01-06 09:56:03,603 INFO sqlalchemy.engine.base.Engine COMMIT 關(guān)于Metadata的更多信息,請(qǐng)查看官方文檔:Schema Definition Language,。 會(huì)話會(huì)話(session)是我們通過SQLAlchemy來操作數(shù)據(jù)庫的入口,。我們前面有介紹過SQLAlchemy的架構(gòu),session是屬于ORM層的,。Session的功能是管理我們的程序和數(shù)據(jù)庫之間的會(huì)話,,它利用Engine的連接管理功能來實(shí)現(xiàn)會(huì)話。我們?cè)谏衔挠刑岬?,我們?chuàng)建了Engine對(duì)象,,但是一般不直接使用它,而是把它交給ORM去使用,。其中,,通過session來使用Engine就是一個(gè)常用的方式。 要是用session,,我們需要先通過 from sqlalchemy.orm import sessionmakerDBSession = sessionmaker(bind=engine)session = DBSession() 我們通過 CRUDCRUD就是CREATE,、READ,、UPDATE,、DELETE,增刪改查,。這個(gè)也是SQLAlchemy中最常用的功能,,而且都是通過上一小節(jié)中的 Create 在數(shù)據(jù)庫中插入一條記錄,是通過session的 new_person = Person(name='new person')session.add(new_person)session.commit() Delete 刪除操作和創(chuàng)建操作差不多,,是把一個(gè)映射類實(shí)例傳遞給 Update 更新一條記錄需要先使用查詢操作獲得一條記錄對(duì)應(yīng)的對(duì)象,,然后修改對(duì)象的屬性,,再通過 Read 查詢操作,,一般稱為query,,在SQLAlchemy中一般是通過Query對(duì)象來完成的。我們可以通過session.query()方法來創(chuàng)建一個(gè)Query對(duì)象,,然后調(diào)用Query對(duì)象的眾多方法來完成查詢操作,。 事務(wù)使用session,就會(huì)涉及到事務(wù),,我們的應(yīng)用程序也會(huì)有很多事務(wù)操作的要求,。當(dāng)你調(diào)用一個(gè)session的方法,導(dǎo)致session執(zhí)行一條SQL語句時(shí),,它會(huì)自動(dòng)開始一個(gè)事務(wù),,直到你下次調(diào)用 會(huì)話,、CRUD,、事務(wù)的小結(jié)上面關(guān)于session,CRUD和事務(wù)的內(nèi)容寫的比較少,,因?yàn)檫@些功能的內(nèi)容很多,,而且官方文檔也寫得很全面,本文就不做一些重復(fù)說明了,。我們會(huì)在下一篇文章中通過webdemo的代碼來看看如何使用這些功能,。 |
|