Python輕量級ORM -- Peewee 筆記(一)

再識ORM

Peewee是一個輕量級Python``ORM庫。對于我捐下,對ORM的經(jīng)歷了反反復(fù)復(fù)的愛恨交織過程到腥。最早對其神奇之處的十分感嘆朵逝;而后又鄙視其生成sql方式;如今乡范,適時適當的在項目中使用ORM配名。

歸根結(jié)底,對ORM態(tài)度的轉(zhuǎn)變篓足,源于對其認識的不足段誊。最早接觸ORM源自Django自帶的ORM,用起來很爽栈拖。當時對sql并不熟悉连舍,ORM了卻了心頭一大痛點。后來使用flask,沒有自帶的ORM索赏,接觸了sqlalchemy盼玄,也是一個很優(yōu)秀的ORM。當時潜腻,我對于ORM的認識還是很淺薄埃儿,大概就認為ORM一個是數(shù)據(jù)庫對象的map,其次就是能夠生成sql查詢融涣。至于如何map童番,如何生成,如何執(zhí)行等威鹿,基本上處于認知上的蠻荒時代剃斧。

直到為了學習Python的元類,并嘗試寫自己的ORM忽你,才算是對ORM有一個完整的認識幼东。以前的認識可以稱之為對其原理和使用方式上的糾結(jié)。不明其根本原理科雳,使用也一知半解「罚現(xiàn)在的認識更多源于工程哲學上方面。使用程序人生的一篇文章軟件隨想錄:代碼與數(shù)據(jù)對ORM工程上的總結(jié)如下:

我們用 ORM糟秘,或者 LINQ简逮,而盡量減少 SQL 的使用,是因為我們通過把SQL這種嵌在代碼里的字符串數(shù)據(jù)蚌堵,轉(zhuǎn)化成了代碼买决,也就意味著我們擁有了編譯時或者運行時的檢查,乃至編輯時的檢查 —— 各種 linter 很難檢測出字符串中的SQL語法或者語義錯誤吼畏,但可以在你撰寫 ORM代碼時便提示你其中蘊含的語法/語義錯誤督赤。這是非常偉大的一個進步 —— 對于軟件工程來說,越早發(fā)現(xiàn)錯誤泻蚊,消弭錯誤所花費的時間就越短躲舌。

誠然,ORM是數(shù)據(jù)層中的更高的抽象性雄,在工程上没卸,抽象程度越高,對于擴展和維護將會更有利秒旋。當然约计,ORM常被人詬病的一大理由就是程式生成的SQL沒有原生的高效。的確迁筛,對于復(fù)雜的SQL語句煤蚌,使用ORM的組合可能比原生的sql更不可讀或者不可維護。此時,我們就需要在項目中繼續(xù)使用raw sql的形式尉桩。

因此筒占,在項目中使用數(shù)據(jù)層的抽象,ORM最好具備以下幾個方面特性:

  1. ORM和數(shù)據(jù)庫可以相互獨立存在蜘犁,ORM可以選擇是否引入關(guān)系約束翰苫。即數(shù)據(jù)庫的字段可以大于ORM中定義的map,有的orm數(shù)據(jù)表字段和orm中的class必須一直这橙,這就在已經(jīng)在的表中引入orm會有點棘手奏窑。
  2. ORM具備提供raw sql的功能。這樣在orm無法方便提供查詢的時候可以使用原生的sql解決問題屈扎。
  3. ORM提供生成數(shù)據(jù)表和根據(jù)數(shù)據(jù)表生成model的特性良哲。這樣就在數(shù)據(jù)的merge時候方便的操作。

基于上述幾點助隧,發(fā)現(xiàn)Pythonpeewee基本滿足我的要求。下面就peewee的簡單使用方式做一個說明滑沧。例子主要取自官網(wǎng)的Quickstart并村。

分為兩個部分,第一部分為基本使用滓技,第二部分為整合到項目(以tornado)為例哩牍。

Peewee 快速開始

快速開始部分根據(jù)官網(wǎng)的文檔為例子。

定義數(shù)據(jù)模型

顧名思義令漂,ORM為數(shù)據(jù)對象關(guān)系的映射膝昆。首先我們需要定義數(shù)據(jù)模型(model)。新建一個文件learn_peewee.py叠必。

import logging
from peewee import MySQLDatabase, Model, CharField, DateField, BooleanField, IntegerField

logger = logging.getLogger('peewee')
logger.setLevel(logging.DEBUG)
logger.addHandler(logging.StreamHandler())

db = MySQLDatabase('test', host='127.0.0.1', user='root', passwd='', charset='utf8', port=3306)

class BaseModel(Model):
    class Meta:
        database = db

class Person(BaseModel):
    name = CharField(verbose_name='姓名', max_length=10, null=False, index=True)
    passwd = CharField(verbose_name='密碼', max_length=20, null=False, default='123456')
    email = CharField(verbose_name='郵件', max_length=50, null=True, unique=True)
    gender = IntegerField(verbose_name='姓別', null=False, default=1)
    birthday = DateField(verbose_name='生日', null=True, default=None)
    is_admin = BooleanField(verbose_name='是否是管理員', default=True)

為了更好的測試學習荚孵,把log打開。首先纬朝,使用MySQLDatabase指定了所連接的數(shù)據(jù)庫test收叶。

然后定義了Person這個表的數(shù)據(jù)字段,如果不指定主鍵共苛,peewee會自動幫我們創(chuàng)建一個id的字段作為主鍵判没。每一個Field都有幾個參數(shù)可以配置,大概就是長度的大小隅茎,是否為空(null)和默認值(default)澄峰,索引(index)和唯一索引(unique)幾個常見的數(shù)據(jù)庫選項。

創(chuàng)建數(shù)據(jù)表

定義好數(shù)據(jù)模型之后辟犀,下一步就是根據(jù)模型創(chuàng)建數(shù)據(jù)表了俏竞。

In [1]: from learn_peewee import *

In [2]: db
Out[2]: <peewee.MySQLDatabase at 0x1114620d0>

In [3]: db.is_closed()
Out[3]: True

In [4]: db.connect()

In [6]: db.is_closed()
Out[6]: False

導(dǎo)入定義的數(shù)據(jù)庫配置和數(shù)據(jù)模型。然后通過db.is_closed函數(shù)查看數(shù)據(jù)庫的連接狀態(tài),使用db.connect函數(shù)創(chuàng)建連接胞此。

?  peewee-orm  netstat -ant | grep -i 3306
tcp4       0      0  127.0.0.1.3306         127.0.0.1.61925        ESTABLISHED
tcp4       0      0  127.0.0.1.61925        127.0.0.1.3306         ESTABLISHED
tcp46      0      0  *.3306                 *.*                    LISTEN

此時確實也能看見mysql的連接創(chuàng)建了臣咖。接下來就能創(chuàng)建數(shù)據(jù)表啦。使用模型的create_table方法漱牵。

In [5]: Person.sqlall()
Out[5]:
('CREATE TABLE `person` (`id` INTEGER AUTO_INCREMENT NOT NULL PRIMARY KEY, `name` VARCHAR(10) NOT NULL, `passwd` VARCHAR(20) NOT NULL, `email` VARCHAR(50), `gender` INTEGER NOT NULL, `birthday` DATE, `is_admin` BOOL NOT NULL)', [])
('CREATE INDEX `person_name` ON `person` (`name`)', [])
('CREATE UNIQUE INDEX `person_email` ON `person` (`email`)', [])
 
In [6]: Person.create_table()
('CREATE TABLE `person` (`id` INTEGER AUTO_INCREMENT NOT NULL PRIMARY KEY, `name` VARCHAR(10) NOT NULL, `passwd` VARCHAR(20) NOT NULL, `email` VARCHAR(50), `gender` INTEGER NOT NULL, `birthday` DATE, `is_admin` BOOL NOT NULL)', [])
('CREATE INDEX `person_name` ON `person` (`name`)', [])
('CREATE UNIQUE INDEX `person_email` ON `person` (`email`)', [])

通過sqlall方法可以看見peewee為我們生成的創(chuàng)建表的sql語句夺蛇。執(zhí)行的時候,log也顯示了將要執(zhí)行的sql酣胀。

如果數(shù)據(jù)表已經(jīng)存在刁赦,執(zhí)行create_table的時候,將會拋出異常闻镶。

default的含義

通過查看上面創(chuàng)建數(shù)據(jù)表的sql可以發(fā)現(xiàn)甚脉,default的行為很古怪。查看數(shù)據(jù)庫的schema铆农,所謂的數(shù)據(jù)表的defalut并不是我們在class中指定的牺氨。對于可以為null的字段。生成的schema中的字段的默認值為null墩剖,對于非null的字段猴凹,生成的默認值為其初始值(零值),即CharField(varchar)為空字串岭皂。InterFiled(int)則為0郊霎。

那么指定default的含義有什么用呢?default用于使用orm插入數(shù)據(jù)的時候爷绘,如果沒有顯示的給字段賦值书劝,那么就會采用default指定的值。使用 create方法插入記錄土至,使用save方法更新記錄购对。

In [2]: p = Person.create(name='master')
('INSERT INTO `person` (`name`, `passwd`, `gender`, `is_admin`) VALUES (%s, %s, %s, %s)', [u'master', u'123456', 1, True])

In [3]: p.save()
('UPDATE `person` SET `name` = %s, `passwd` = %s, `gender` = %s, `is_admin` = %s WHERE (`person`.`id` = %s)', [u'master', u'123456', 1, True, 3])
Out[3]: 0L

可以看到,我們只指定了name字段的值毙籽,因為passwdgender還有is_admin字段都指定default洞斯,此時插入的時候,就使用了默認的值坑赡。調(diào)用save方法的時候烙如,執(zhí)行是update語句,因此也同樣執(zhí)行了默認值毅否。而沒有指定默認值的字段亚铁,peewee則直接忽略。

In [4]: p.email = 'master@g.com'

In [5]: p.save()
('UPDATE `person` SET `name` = %s, `passwd` = %s, `email` = %s, `gender` = %s, `is_admin` = %s WHERE (`person`.`id` = %s)', [u'master', u'123456', u'master@g.com', 1, True, 3])
Out[5]: 1L
In [8]: p.passwd = '11'

In [9]: p.save()
('UPDATE `person` SET `name` = %s, `passwd` = %s, `email` = %s, `gender` = %s, `is_admin` = %s WHERE (`person`.`id` = %s)', [None, u'11', u'master@g.com', 1, True, 3])
Out[9]: 1L

In [8]: p = Person.get(id=1)
('SELECT `t1`.`id`, `t1`.`name`, `t1`.`passwd`, `t1`.`email`, `t1`.`gender`, `t1`.`birthday`, `t1`.`is_admin` FROM `person` AS t1 WHERE (`t1`.`id` = %s) LIMIT 1 OFFSET 0', [1])

In [9]: p.save()
('UPDATE `person` SET `name` = %s, `passwd` = %s, `email` = %s, `gender` = %s, `birthday` = %s, `is_admin` = %s WHERE (`person`.`id` = %s)', [u'master', u'11', u'master@g.com', 1, None, True, 1])
Out[9]: 0L

有默認值的字段螟加,如果指定了值徘溢,就使用所賦的值吞琐。新讀的數(shù)據(jù),就不用考慮默認值了然爆,如果讀取的值沒有變站粟,更新的時候就依然采用這個值,與default值沒有關(guān)系曾雕。

總結(jié)

  1. default的設(shè)置與創(chuàng)建數(shù)據(jù)表的schema沒有關(guān)系奴烙。僅與插入的時候有關(guān)系。
  2. 插入的時候剖张,如果字段設(shè)置了default值切诀,則會按照default指定的值插入,如果沒有指定搔弄,同時字段可以為null幅虑,則數(shù)據(jù)庫自動初始化值為null,如果字段不能為null顾犹,則數(shù)據(jù)庫自動初始化為其零值倒庵。
  3. 最佳實踐,如果字段為非Null炫刷,最好設(shè)置default值哄芜,同時數(shù)據(jù)庫schema也設(shè)置其default值,如果字段為可以為null柬唯,那么初始值就設(shè)置為null即可。

快速認識了Peewee圃庭,接下來討論peewee更強大的功能锄奢,例如定義關(guān)系和增刪改查。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末剧腻,一起剝皮案震驚了整個濱河市拘央,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌书在,老刑警劉巖灰伟,帶你破解...
    沈念sama閱讀 217,509評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異儒旬,居然都是意外死亡栏账,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評論 3 394
  • 文/潘曉璐 我一進店門栈源,熙熙樓的掌柜王于貴愁眉苦臉地迎上來挡爵,“玉大人,你說我怎么就攤上這事甚垦〔杈椋” “怎么了涣雕?”我有些...
    開封第一講書人閱讀 163,875評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長闭翩。 經(jīng)常有香客問我挣郭,道長,這世上最難降的妖魔是什么疗韵? 我笑而不...
    開封第一講書人閱讀 58,441評論 1 293
  • 正文 為了忘掉前任兑障,我火速辦了婚禮,結(jié)果婚禮上伶棒,老公的妹妹穿的比我還像新娘旺垒。我一直安慰自己,他們只是感情好肤无,可當我...
    茶點故事閱讀 67,488評論 6 392
  • 文/花漫 我一把揭開白布先蒋。 她就那樣靜靜地躺著,像睡著了一般宛渐。 火紅的嫁衣襯著肌膚如雪竞漾。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,365評論 1 302
  • 那天窥翩,我揣著相機與錄音业岁,去河邊找鬼。 笑死寇蚊,一個胖子當著我的面吹牛笔时,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播仗岸,決...
    沈念sama閱讀 40,190評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼允耿,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了扒怖?” 一聲冷哼從身側(cè)響起较锡,我...
    開封第一講書人閱讀 39,062評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎盗痒,沒想到半個月后蚂蕴,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,500評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡俯邓,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,706評論 3 335
  • 正文 我和宋清朗相戀三年骡楼,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片稽鞭。...
    茶點故事閱讀 39,834評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡君编,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出川慌,到底是詐尸還是另有隱情吃嘿,我是刑警寧澤祠乃,帶...
    沈念sama閱讀 35,559評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站兑燥,受9級特大地震影響亮瓷,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜降瞳,卻給世界環(huán)境...
    茶點故事閱讀 41,167評論 3 328
  • 文/蒙蒙 一嘱支、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧挣饥,春花似錦除师、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至短荐,卻和暖如春倚舀,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背忍宋。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評論 1 269
  • 我被黑心中介騙來泰國打工痕貌, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人糠排。 一個月前我還...
    沈念sama閱讀 47,958評論 2 370
  • 正文 我出身青樓舵稠,卻偏偏與公主長得像,于是被迫代替她去往敵國和親入宦。 傳聞我的和親對象是個殘疾皇子柱查,可洞房花燭夜當晚...
    茶點故事閱讀 44,779評論 2 354

推薦閱讀更多精彩內(nèi)容