peewee 是一個輕量級的 Python ORM 框架,個人用下來感覺還好,簡單易上手,對於小項目能滿足大部分需求。peewee 的官方文檔沒有中文版的,網上的文章都是快速入門,抄了文檔中幾個例子,沒有詳細介紹的。本文略長,詳細介紹了增刪改查操作,並且詳解介紹了使用過程中遇到的各種問題。 ...
說明:peewee 中有很多方法是延時執行的,需要調用
execute()
方法使其執行。下文中不再特意說明這個問題,大家看代碼。
本文中代碼樣例所使用的 Person 模型如下:
class Person(Model):
Name = CharField()
Age = IntegerField()
Birthday = DateTimeField()
Remarks = CharField(null=True)
一、新增
1、create
Model.create
向資料庫中插入一條記錄,並返回一個新的實例。
p = Person.create(Name='張三', Age=30, Birthday=date(1990, 1, 1))
2、save
語法:
save(force_insert=False, only=None)
參數:
- force_insert:是否強制插入
- only(list):需要持久化的欄位,當提供此參數時,只有提供的欄位被持久化。
示例:
p1 = Person(Name='王五', Age=50, Birthday=date(1970, 1, 1))
p1.save()
這裡說的比較簡單,下麵會詳細說明。
3、insert
insert
只插入數據而不創建模型實例,返回新行的主鍵。
Person.insert(Name='李四', Age=40, Birthday=date(1980, 1, 1)).execute()
4、insert_many
語法:
insert_many(rows, fields=None)
參數:
- rows:元組或字典列表,要插入的數據
- fields(list):需要插入的欄位名列表。
說明:
1、當 rows 傳遞的是字典列表時,fields 是不需要傳的,如果傳了,那麼,rows 中的欄位在字典中必須存在,否則報錯。如果沒有傳遞 fields 參數,那麼預設取所有字典的交集作為插入欄位。這個也好理解,比如一個字典的鍵是a、b、c
,一個是b、c、d
,那麼就取b、c
作為需要插入的欄位。peewee 不會為缺失的欄位做預設處理。
2、當 rows 傳遞的是元組列表時,必須指定 fields,並且 fields 中欄位名的順序跟元組一致。元組中值的數量必須大於等於 fields 中欄位的數量,一般建議是保持一致。
示例:
Person.insert_many([
('張三', 30, date(1990, 1, 1)),
('李四', 40, date(1980, 1, 1)),
('王五', 50, date(1970, 1, 1))
],
['Name', 'Age', 'Birthday']
).execute()
Person.insert_many([
{'Name': '張三', 'Age': 30, 'Birthday': date(1990, 1, 1)},
{'Name': '李四', 'Age': 40, 'Birthday': date(1980, 1, 1)},
{'Name': '王五', 'Age': 50, 'Birthday': date(1970, 1, 1)}
]
).execute()
對於批量操作,應該放在事務中執行:
with db.atomic():
Person.insert_many(data, fields=fields).execute()
在使用批量插入時,如果是 SQLite,SQLite3 版本必須為 3.7.11.0 或更高版本才能利用批量插入API。此外,預設情況下,SQLite 將 SQL 查詢中的綁定變數數限製為 999。
SQLite 中,當批量插入的行數超過 999 時,就需要使用迴圈來將數據批量分組:
with db.atomic():
for idx in range(0, len(data), 100):
Person.insert_many(data[idx: idx+100], fields=fields).execute()
Peewee 中帶有一個分塊輔助函數 chunked()
,使用它可以有效地將通用迭代塊分塊為一系列批量迭代的迭代:
from peewee import chunked
# 一次插入 100 行.
with db.atomic():
for batch in chunked(data, 100):
Person.insert_many(batch).execute()
5、bulk_create
語法:
bulk_create(model_list, batch_size=None)
參數:
- model_list (iterable):未保存的模型實例的列表或其他可迭代對象。
- batch_size (int):每次批量插入的行數。如果未指定,則一次性全部插入。
示例:
簡單來說,insert_many
使用字典或元組列表作為參數,而 model_list
使用模型實例列表作為參數,就這區別。
data = [Person(Name='張三~', Age=30, Birthday=date(1990, 1, 1)),
Person(Name='李四~', Age=40, Birthday=date(1980, 1, 1))]
with db.atomic():
Person.bulk_create(data)
註意:如果使用的是 Postgresql(支持該RETURNING子句),則先前未保存的模型實例將自動填充其新的主鍵值。
例如用的是 SQLite,執行上述代碼之後,print(data[0].id)
顯示的結果是 None
。
6、batch_commit
這不是一個好的方法,來看下麵的例子
data_dict = [{'Name': '張三', 'Age': 30, 'Birthday': date(1990, 1, 1)},
{'Name': '李四', 'Age': 40, 'Birthday': date(1980, 1, 1)},
{'Name': '王五', 'Age': 50, 'Birthday': date(1970, 1, 1)}]
for row in db.batch_commit(data_dict, 100):
p = Person.create(**row)
查看 SQL 語句如下:
('BEGIN', None)
('INSERT INTO "person" ("Name", "Age", "Birthday") VALUES (?, ?, ?)', ['張三', 30, datetime.date(1990, 1, 1)])
('INSERT INTO "person" ("Name", "Age", "Birthday") VALUES (?, ?, ?)', ['李四', 40, datetime.date(1980, 1, 1)])
('INSERT INTO "person" ("Name", "Age", "Birthday") VALUES (?, ?, ?)', ['王五', 50, datetime.date(1970, 1, 1)])
其實,batch_commit
就是自動添加了一個事務,然後一條條的插入,所以返回的模型實例中能獲取到主鍵。
參數第一個是字典列表,第二個就是每多少條啟用一個事務,大家可以把它改成 1 看下 SQL 語句就明白了。
7、insert_from
使用 SELECT 查詢作為源 INSERT 數據。此 API 應用於 INSERT INTO … SELECT FROM … 形式的查詢。
語法:
insert_from(query, fields)
參數:
- query:SELECT查詢用作數據源
- fields:要將數據插入的欄位,此參數必須要的
示例:我們將 Person 表按原結構複製一個 Person2 表出來,以做演示。
data = Person.select(Person.Name, Person.Age, Person.Birthday)
Person2.insert_from(data, ['Name', 'Age', 'Birthday']).execute()
註意: 因為是 INSERT INTO … SELECT FROM … 形式的,所以數據源的列跟要插入的列必須保持一致。
二、刪除
1、delete
delete
後加 where
刪除指定記錄,如果不加 where
,則刪除全部記錄。
Person.delete().where(Person.Name=='王五').execute()
2、delete_instance
刪除給定的實例。
語法:
delete_instance(recursive=False, delete_nullable=False)
示例: