聲明
本篇主要講,關于peewee的一些拓展:
包括新式CRUD-數據導入導出-信號-數據庫反向生成模型。
擴展官檔:http://docs.peewee-orm.com/en...
作者友好 與 peewee提問方式
當我用到拓展模塊的 新CRUD時,文檔給的內容少之又少。
因為拓展的新CRUD是真的方便好用,和(PyMongo的用法差不多)
但是功能卻不全。并且與我們第二篇,講的CRUD又不兼容。
所以在難以取舍之際, 我選擇了提問。
peewee作者在官檔中詳細說到。 如果你有問題或疑惑可以通過以下兩種方式:
- 去stack overflow 提問題,標簽打上python 和 peewee。 peewee作者會不定期瀏覽并回答你給你幫助。
- 去https://groups.google.com/gro...,這個google群組提問。(需要科學上網)
所以我選擇了去 stack overflow提問。
我問題發出去,應該是不到一小時,作者就給回復了,我驚了。。(發完我就睡覺了,第二天起來才看到)
提問內容傳送門如下:https://stackoverflow.com/que...
作者回復的意思是:
拓展的playhouse.dataset里面的DataSet 的 新式CRUD API 的涉及初衷就是為了簡單使用。
但它并不會代替 核心CRUD (就是我們第二篇講的CRUD)
并且,它設計的初衷就是讓我們可以方便 (json/csv格式的數據 與 數據庫的數據 相互導入或導出)
我說的這些操作,下面都會寫到。
所以說,該提問就提問,你收獲了peewee知識的同時,又能增加peewee的社區活躍度。
playhouse擴展模塊的DataSet
我們前2篇文章就用了這一行代碼就可以導入所有,因為所有基本功能都集成在 peewee下
from peewee import *
但接下來講的是擴展,而擴展就是新分支了,與peewee沒關系了
from playhouse.dataset import DataSet
數據庫連接
進入正題,連接MySQL,你可以有兩種連接方式:
# 強調一下,官檔中給出 DataSet 是在 playhouse.dataset 下
# 我再強調一下, 是 DataSet , 而不是 DataBase !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
from playhouse.dataset import DataSet
db = DataSet('mysql://root:Jilin963389970@39.107.86.223:3306/test')
官檔-所有數據庫的連接示例: http://docs.peewee-orm.com/en...
數據庫與表的基本操作
創建表
owner = db['owner']
# 后面這個"owner" 是你自己指定的key, 也就是你指定的 "表名"
# 前面這個 owner 變量用來接受返回結果,它就是一個實例化的表對象。 一會我們用它做一系列操作
我講完了,表已經創建好了, (你臉上有沒有出現問號。。) 驚不驚喜,意不意外??
題外話:
如果你用過,pymongo來使用MongoDB, 那這種原理應該不陌生的。
那 owner = db['owner'] 這一行代碼究竟做了什么?
1. 它會去數據庫,創建一個 名為 owner的表 (有則返回,無則創建)
2. 不但創建了表, 而且它還在表中 自動創建了一個 id (int型,主鍵,自增)
列出表 & 列出字段
列出表(等價于MySQL中的命令: show tables)
print(db.tables)
>> ['new_owner', 'owner']
列出表中的字段(等價于MySQL中的命令: show columns):
table = db['owner'] # 先選擇一個表。
print(table.columns)
>> ['id', 'name', 'age', 'gender', 'hobby', 'nickname']
統計表中記錄數(就是行數)
print(len(table))
>> 10
事務(transaction)
其實我在第一篇,已經講過事務了
事務-傳送門: https://segmentfault.com/a/11...
當時我是用的 MySQLDatabase 連接工具。
我只講了一種使用方法: db.atomic()
其實還有另外一種使用方法,就是 db.transaction()
這兩種方法差不多, 你可以這么認為,就是把 atomic 和 transaction 單詞換一下,用法一模一樣
而現在我們是使用的 DataSet 連接工具 (開篇我強調過):
db = DataSet(....)
DataSet 只提供給我們 transaction()這種用法, 而沒有提供 atomic()
但我說了,這兩種用法你可以認為只是單詞換了一下。用法一樣的。
因此你完全可以直接看我前面給的 "事務-傳送門" 這篇之前寫的文章。
CRUD (這種CRUD方式我不太推薦)
- 我們第二章詳細講過全套的CRUD,其實那就夠了。
- 而本章這個拓展的CRUD,完全是另一種模式。另一個模塊的東西。
- 但我不推薦用,因為感覺還沒成熟,不完善(很多CRUD細節功能沒有), 官檔給的也粗略。
- 此外,下面涉及到與 本套CRUD有關聯的操作, 我下面統稱為 "拓展的CRUD"
增加數據
owner.insert(name='Alice', age=20)
owner.insert(name='Zhang', age=18)
這看起來沒什么問題,我們之前講的差不多。但你有沒有意識到幾個問題:
1. owner表 是自動創建出來的,它只有一個主鍵。
2. 我們沒有創建 name 和 age 字段
3. 既然沒有創建字段,為什么可以插入數據???
解惑:
1. 我們調用第一個 insert()的時候, 它就會自動幫我們 去數據庫創建對應參數的字段(固定了):
數據類型關系對照如下:
python數據類型 MySQL數據類型
int int(11) 允許為空 默認值未空
str text 允許為空 默認值未空
那peewee為我們自動創建的字段如下:
+-------+---------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | text | YES | | NULL | |
| age | int(11) | YES | | NULL | |
+-------+---------+------+-----+---------+----------------+
2. 創建后,它還會自動為我們根據我們傳的參數, 對應的插入 值
3. 特別注意,特別注意:
假如我們插入這樣一條錯誤"類型"的數據:
owner.insert(name=18, age=18) # 按理說name應該為 text型,age應該為整形
雖然我們name給的整形,但,peewee內部會自動為我們轉為將 name轉為字符串類型
同理 字符串也會自動幫我們轉為整形(對應數據庫中的類型)。 但'abc'這樣的轉不了哦
修改數據
owner.update(name='Alice', age='50')
這行代碼為我們做了如下事情:
將"所有"行數據, name改為 Alice, age改為 50
owner.update(name='Alice', age='50',columns=['name'])
多加了一個 columns參數,為我們做了如下事情:
先看最后的 columns=['name'] : 其實他就等價于 where name = xxx
其實意思就是以 "name字段為條件" 修改數據:(而name我們已經給了 = 'Alice')
具體過程如下:
1. 找到 name = Alice, 的所有記錄
2. 將 name= Alice 的記錄 name改為Alice(相當于沒變), age 改為 50
owner.update(gender='man')
你仔細看看,我們之前是沒有 gender這個字段的。 如果更新未創建字段,它會為我們做如下事情:
1. 它會自動為我們在這個表中創建一個 這個 "gender新字段"
2. 并且,"所有行記錄" 都會被賦予成 "man" 值。 注意是 所有行,所有行,所有行
3. 如果你只想"給指定記錄部分"賦予 "man"值, 那你可以加一個我們之前說的 "columns參數"
自然而然地,其他 "未通過 columns指定的記錄" 就會被賦予 NULL 值。
查詢數據
首先說一下它這個查詢的特色。
- 可以對查詢結果進行 切片,索引操作(和python的切片和索引是一模一樣的)
- 索引之后自動轉為 列表嵌字典 [{}, {}]
- 下面3種方式,也都支持 切片和索引(我就不舉例了,很簡單)
方式1:all() 獲取全部數據
owner = db1['owner']
query = owner.all()
for obj in query:
print(obj) # 遍歷每條記錄, 結果是 字典 類型
方式2:find() 查詢符合條件的所有數據
query = owner.find(name='Tom') # 查詢名為 Tom的數據
for obj in query:
print(obj) # 遍歷每條記錄, 結果是 字典 類型
方式3:find_one() 查詢符合條件的 第一條 數據
print(owner.find_one(name='Tom'))
注意一下,find_one 查出來結果就是一條字典。 不要再遍歷了,遍歷就出事了。。。
刪除數據
result = owner.delete(name='Tom')
說明:
1. 若 delete() 不指定參數,那么即為全部刪除。。慎用
2. 返回值result的值, 代表刪除數據的條數
Json/CSV數據 導入到數據庫(只能聯用拓展的CRUD)
Json實例
我在當前目錄下創建一個 new_owner.json, 內容如下:
[
{"name": "Alice", "age": 18},
{"name": "Zhang", "age": 30}
]
主文件代碼如下:
new_owner = db['new_owner'] # 新建一張表,名為 new_owner
new_owner.thaw(
filename='new_owner.json', # 就是上面的那個 json文件
format='json' # 指定格式為json, 默認值是 csv
)
這樣就導入好了,數據庫內容如下:
+----+------+-------+
| id | age | name |
+----+------+-------+
| 1 | 18 | Alice |
| 2 | 30 | Zhang |
+----+------+-------+
特殊情況分析: 假如基于上面創建好的數據庫與數據, 將剛剛的json文件稍加改動,加個 "gender"
[
{"name": "Alice", "age": 18, "gender": "man"},
{"name": "Zhang", "age": 30}
]
假如我們還是用上面的一模一樣的主文件代碼(我這里就不重復寫了),導入剛修改的 新json數據。
我們分析一下:
我說了,是在原有表 和 字段的 基礎上去導入新數據。 (原有數據庫字段為 name 和 age)
但是今時不同往日。。。 我們剛剛的json新添加了一個, gender。
而原有的數據庫中,并沒有這個字段。 如果我們還是用之前的這個代碼:
new_owner.thaw(
filename='new_owner.json', # 就是上面的那個 json文件
format='json' # 指定格式為json, 默認值是 csv
)
那么,peewee會自動把這個 新 gender 鍵,同步到數據庫,并生成 新字段 gender
+----+------+-------+--------+
| id | age | name | gender |
+----+------+-------+--------+
| 1 | 18 | Alice | NULL |
| 2 | 30 | Zhang | NULL |
| 3 | 18 | Alice | man |
| 4 | 30 | Zhang | NULL |
+----+------+-------+--------+ 看見了把,gender字段,會自動生成。
但是在某種情況下, 你并不想讓數據庫 創建這個 新字段
假如json中 "gender" , 它屬于一個臟數據,我們不需要他,而是單純的想插入 name和age數據。
那么你可以,添加一個 strict=True 參數:
new_owner.thaw(
filename='new_owner.json',
format='json',
strict=True # 看這里,添加一個這個 strict=True就好了
)
strict=True意味著,它會對照json的key 與 數據庫的字段, 并以數據庫的字段為主,嚴格匹配插入。
CSV示例
其實和json的幾乎差不多,注意csv格式,逗號分隔,設置表頭。 new_owner.csv 內容如下:
name,age
Alice,18
Zhang,30
主文件代碼如下
new_owner.thaw(
filename='owner.csv',
# format='csv' # 我說過,format默認參數就是csv,所以給不給format參數都行。
# strict=True # 嚴格匹配字段插入, 和上面講的json是一模一樣的作用。
)
最后說個小細節,不知道你有沒有注意,我們json和csv文件, 都沒有指定id。
雖然沒有指定,但是 peewee 同樣會為我們自動創建 id (同樣也是 int(11), 主鍵, 自增)
數據庫數據導入到Json/CSV(只能聯用拓展的CRUD)
導入都說完了,導出就更簡單了。。就API變個名的事:
導出單個表的全部數據:
owner = db['owner']
owner.freeze(
filename='new_owner.json',
format='json', # 指定格式為json, 你要是不指定,默認值是 csv
)
當然,這里有個特色,導出方式還可以 導出某個查詢結果!!
query = owner.find(name='Tom')
db.freeze(
query, # 查詢結果,注意這個查詢結果,必須是個查詢集類型。
filename='new_owner.json',
format='json', # 指定格式為json, 你要是不指定,默認值是 csv
)
中場暫停。。。
至此,playhouse.dataset的DataSet里面的 新版CRUD,及其附屬功能(json導入導出等)講完。
也許你會很不適應。 (這新版的CRUD,如果實在不能掌握,就當了解即可)
最重要的還是第二篇文章的CRUD:https://segmentfault.com/a/11...
以下要講的,就是 第二篇文章講的,正常的 (from peewee import * )里面的 CRUD 相關的操作了。
信號(Signal)
官方只設定了如下 4 種信號:
一、pre_save: 保存之前調用
二、post_save:保存之后調用
pre_save 和 post_save只支持下面兩種API:
1. create() # 創建數據
觸發:
Owner.create(name='Tom') # create包含了 save() ,所以會自動觸發
2. save() # 更新數據
觸發:
obj = Owner.get(name='Butch')
obj.name = 'Alice'
obj.save()
注意: 你想用保存信號,就必須用這兩種API, 用 update()是不好使的哦!!!!!!!!!!
三、pre_delete: 刪除數據之前調用
四、post_delete:刪除數據之后調用
pre_delete 和 post_delete 只支持一種 API ,那就是 delete_instance()
觸發:
objs = Owner.select().where(Owner.name=='lin')
for obj in objs:
obj.delete_instance()
接下來我們開始看代碼如何寫:
先把表和數據構造出來,還是老方式:
from peewee import *
# 注意是 playhouse里的Model, 以及前面提到的 4 種 信號
from playhouse.signals import Model, post_save, pre_save, pre_delete, post_delete
db = MySQLDatabase('test', user='root', password='123',
host='IP', port=3306, charset='utf8mb4')
class Owner(Model):
"""
特別注意,這個Model, 使用是playhouse.signals下的 Model
而不是 peewee 下的 Model, 這需要特別注意
兩個模塊都有Model, 所以把用的Model放在 相對偏下面導入
"""
data = IntegerField() # 而字段依然是 from peewee import * 導入的
class Meta:
database=db
db.create_tables([Owner])
進入正題:信號使用有兩種方式:
方式1(裝飾器方式):
# 前面的4種信號的用法就是用來,裝飾一個自定義函數。
# 這個自定義函數就是信號出發之后,為我們做事的。
# 接下來我以 post_save 為例 (當然我這個例子只是強調一下語法,并沒有實用價值)
@pre_save(sender=Owner) # sender指定 我們的模型類
def aaa(model_class, instance, created=True): # 這個名字隨便起
"""
model_class: 就是 Owner這個類,默認傳進來方便你使用
instance 和 create=True 照著寫上就行不用管它
"""
print(f"數據入庫之前我們捕獲了此表名=>{model_class}")
for obj in model_class.select().dicts(): # model_class就是Owner類,它可以任意CRUD
print(f"再次查看此表信息{obj}")
觸發信號:
obj = Owner.get(name='Tom')
obj.name = 'Rose'
obj.save()
方式2 (函數連接):
信號-官檔:http://docs.peewee-orm.com/en...
其實你用 方式1 就行了。
數據庫反向生成 Python模型類
參數官檔:http://docs.peewee-orm.com/en...
看好參數大小寫就行(test是我的數據庫名):
python -m pwiz -e mysql -H 192.6.6.6 -p 3306 -u root -P test > mymodel.py
# 指定了 -P ,密碼是后續命令行 追入的。
模型遷移 migration
我沒怎么看,需要的自己瞅瞅。。
模型遷移-官檔:http://docs.peewee-orm.com/en...
結束語
其實ORM都差不多。
Django:的ORM還算可以。但是不太好脫離框架單獨使用 (相當于與Django平級)。
sqlalchemy:沒怎么用過。之前看過幾眼。感覺極度不適。感覺學習成本有點高(相當于與Python平級)
peewee:是一個可單獨使用的簡便的ORM框架(寫web爬蟲之類的都能用得上,相當于與Python平級)
我感覺:如果ORM的學習成本大于SQL的學習成本, 那倒不如精修一下SQL,即使換了環境也能用得上。
有時候高階ORM用多了,可能連SQL都不會寫了。。。 (It's just for me......)
第一篇傳送門:https://segmentfault.com/a/11...
第二篇傳送門:https://segmentfault.com/a/11...
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
