說明:peewee 中有不少方法是延時執行的,須要調用
execute()
方法使其執行。下文中再也不特地說明這個問題,你們看代碼。php
本文中代碼樣例所使用的 Person 模型以下:css
class Person(Model):
Name = CharField()
Age = IntegerField()
Birthday = DateTimeField()
Remarks = CharField(null=True)
Model.create
向數據庫中插入一條記錄,並返回一個新的實例。python
p = Person.create(Name='張三', Age=30, Birthday=date(1990, 1, 1))
語法:nginx
save(force_insert=False, only=None)
參數:sql
示例:數據庫
p1 = Person(Name='王五', Age=50, Birthday=date(1970, 1, 1))
p1.save()
這裏說的比較簡單,下面會詳細說明。bash
insert
只插入數據而不建立模型實例,返回新行的主鍵。app
Person.insert(Name='李四', Age=40, Birthday=date(1980, 1, 1)).execute()
語法:函數
insert_many(rows, fields=None)
參數:測試
說明:
一、當 rows 傳遞的是字典列表時,fields 是不須要傳的,若是傳了,那麼,rows 中的字段在字典中必須存在,不然報錯。若是沒有傳遞 fields 參數,那麼默認取全部字典的交集做爲插入字段。這個也好理解,好比一個字典的鍵是a、b、c
,一個是b、c、d
,那麼就取b、c
做爲須要插入的字段。peewee 不會爲缺失的字段作默認處理。
二、當 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()
語法:
bulk_create(model_list, batch_size=None)
參數:
示例:
簡單來講,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
。
這不是一個好的方法,來看下面的例子
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 語句就明白了。
使用 SELECT 查詢做爲源 INSERT 數據。此 API 應用於 INSERT INTO … SELECT FROM … 形式的查詢。
語法:
insert_from(query, fields)
參數:
data = Person.select(Person.Name, Person.Age, Person.Birthday)
Person2.insert_from(data, ['Name', 'Age', 'Birthday']).execute()
注意: 由於是 INSERT INTO … SELECT FROM … 形式的,因此數據源的列跟要插入的列必須保持一致。
delete
後加 where
刪除指定記錄,若是不加 where
,則刪除所有記錄。
Person.delete().where(Person.Name=='王五').execute()
刪除給定的實例。
語法:
delete_instance(recursive=False, delete_nullable=False)
示例:
p = Person.get(Person.Name=='張三')
p.delete_instance()
delete_instance
直接執行刪除了,不用調用execute()
方法。
參數:
通常我都是先講參數再講示例的,此次倒過來,示例其實很簡單,一看就明白。可是這個參數缺須要好好講下。
這兩個參數都跟外鍵有關。咱們修改一下測試用的模型。假設有這樣兩個模型,一我的員,一個部門,人員屬於部門。
class Department(Model):
Name = CharField()
class Meta:
database = db
class Person(Model):
Name = CharField()
Age = IntegerField()
Birthday = DateTimeField()
Remarks = CharField(null=True)
Department = ForeignKeyField(Department, null=True) # 這裏外鍵可爲空和不可爲空是不同的,下面說明
class Meta:
database = db
① 當 recursive=False
時,只刪除了【部門】,【人員】沒有影響,從 SQL 語句中能夠看出。
d = Department.get(1)
d.delete_instance(recursive=False)
# 執行的 SQL 語句
('SELECT "t1"."id", "t1"."Name" FROM "department" AS "t1" WHERE ? LIMIT ? OFFSET ?', [1, 1, 0])
('DELETE FROM "department" WHERE ("department"."id" = ?)', [1])
② 當 recursive=True
,而且外鍵不可爲空時,會先刪除【部門】下的【人員】,再刪除【部門】。
d = Department.get(1)
d.delete_instance(recursive=True)
# 執行的 SQL 語句
('SELECT "t1"."id", "t1"."Name" FROM "department" AS "t1" WHERE ? LIMIT ? OFFSET ?', [1, 1, 0])
('DELETE FROM "person" WHERE ("person"."Department_id" = ?)', [1])
('DELETE FROM "department" WHERE ("department"."id" = ?)', [1])
③ 當 recursive=True
,而且外鍵可爲空時,先將【人員】的【部門ID(外鍵字段)】置爲了 NULL,再刪除【部門】。
d = Department.get(1)
d.delete_instance(recursive=True)
# 執行的 SQL 語句
('SELECT "t1"."id", "t1"."Name" FROM "department" AS "t1" WHERE ? LIMIT ? OFFSET ?', [1, 1, 0])
('UPDATE "person" SET "Department_id" = ? WHERE ("person"."Department_id" = ?)', [None, 1])
('DELETE FROM "department" WHERE ("department"."id" = ?)', [1])
④ delete_nullable
僅在 recursive=True
且外鍵可爲空時有效,和 ③ 同樣,當 delete_nullable=True
時,會刪除【人員】,而不是將【人員的部門ID】置爲 NULL。
d = Department.get(1)
d.delete_instance(recursive=True, delete_nullable=True)
# 執行的 SQL 語句
('SELECT "t1"."id", "t1"."Name" FROM "department" AS "t1" WHERE ? LIMIT ? OFFSET ?', [1, 1, 0])
('DELETE FROM "person" WHERE ("person"."Department_id" = ?)', [1])
('DELETE FROM "department" WHERE ("department"."id" = ?)', [1])
以前說過,save()
方法能夠插入一條記錄,一旦模型實例具備主鍵,任何後續調用 save()
都將致使 UPDATE 而不是另外一個 INSERT。模型的主鍵不會改變。
p = Person(Name='王五', Age=50, Birthday=date(1970, 1, 1))
p.save()
print(p1.id)
p.Remarks = 'abc'
p.save()
這個例子,第一次執行的 save
是 INSERT,第二次是 UPDATE。
這裏解釋一下,
Person
這個模型,我並無指定主鍵,peewee 會自動增長一個名爲 id 的自增列做爲主鍵。在執行第一個save()
方法的時候,主鍵沒值,因此執行 INSERT,save()
方法執行以後,自增列的值就返回並賦給了模型實例,因此第二次調用save()
執行的是 UPDATE。
若是模型中一開始就用PrimaryKeyField
或primary_key
指定了主鍵,那麼save
執行的永遠都是update
,因此什麼主鍵不存在則 INSERT,存在則 UPDATE 這種操做根本不存在,只能本身來寫判斷。
update
用於批量更新,方法相對簡單,如下三種寫法均可以
# 方法一
Person.update({Person.Name: '趙六', Person.Remarks: 'abc'}).where(Person.Name=='王五').execute()
# 方法二
Person.update({'Name': '趙六', 'Remarks': 'abc'}).where(Person.Name=='張三').execute()
# 方法三
Person.update(Name='趙六', Remarks='abc').where(Person.Name=='李四').execute()
看這樣的一個需求,有一張表,記錄博客的訪問量,每次有人訪問博客的時候,訪問量+1。
由於懶得新建模型,咱們就以 Person 模型的 Age + 1 來演示。
咱們能夠這樣來寫:
for p in Person.select():
p.Age += 1
p.save()
這樣固然是能夠實現的,可是這不只速度慢,並且若是多個進程同時更新計數器,它也容易受到競爭條件的影響。
咱們能夠用 update
方法來實現。
Person.update(Age=Person.Age+1).execute()
Model.get()
方法檢索與給定查詢匹配的單個實例。
語法:
get(*query, **filters)
參數:
示例:
p1 = Person.get(Name='張三')
或者
p2 = Person.get(Person.Name == '李四')
當獲取的結果不存在時,報 Model.DoesNotExist
異常。若是有多條記錄知足條件,則返回第一條。
若是當獲取的結果不存在時,不想報錯,可使用 Model.get_or_none()
方法,會返回 None
,參數和 get
方法一致。
對於主鍵查找,還可使用快捷方法Model.get_by_id()
。
Person.get_by_id(1)
Peewee 有一個輔助方法來執行「獲取/建立」類型的操做: Model.get_or_create()
首先嚐試檢索匹配的行。若是失敗,將建立一個新行。
p, created = Person.get_or_create(Name='趙六', defaults={'Age': 80, 'Birthday': date(1940, 1, 1)})
print(p, created)
# SQL 語句
('SELECT "t1"."id", "t1"."Name", "t1"."Age", "t1"."Birthday", "t1"."Remarks" FROM "person" AS "t1" WHERE ("t1"."Name" = ?) LIMIT ? OFFSET ?', ['趙六', 1, 0])
('BEGIN', None)
('INSERT INTO "person" ("Name", "Age", "Birthday") VALUES (?, ?, ?)', ['趙六', 80, datetime.date(1940, 1, 1)])
參數:get_or_create
的參數是 **kwargs
,其中 defaults 爲非查詢條件的參數,剩餘的爲嘗試檢索匹配的條件,這個看執行時的 SQL 語句就一目瞭然了。對於「建立或獲取」類型邏輯,一般會依賴惟一 約束或主鍵來防止建立重複對象。但這並非強制的,好比例子中,我以 Name
爲條件,而 Name
並不是主鍵。只是最好不要這樣作。
返回值:get_or_create
方法有兩個返回值,第一個是「獲取/建立」的模型實例,第二個是是否新建立。
使用 Model.select()
查詢獲取多條數據。select
後能夠添加 where
條件,若是不加則查詢整個表。
語法:
select(*fields)
參數:
示例:
ps = Person.select(Person.Name, Person.Age).where(Person.Name == '張三')
select()
返回結果是一個 ModelSelect
對象,該對象可迭代、索引、切片。當查詢不到結果時,不報錯,返回 None
。而且 select()
結果是延時返回的。若是想當即執行,能夠調用 execute()
方法。
注意:
where
中的條件不支持Name='張三'
這種寫法,只能是Person.Name == '張三'
。
使用 .count()
方法能夠獲取記錄條數。
Person.select().count()
也許你會問,用 len()
方法能夠嗎?固然也是能夠的,可是是一種不可取的方法。
len(Person.select())
這二者的實現方式天差地遠。用 count()
方法,執行的 SQL 語句是:
('SELECT COUNT(1) FROM (SELECT 1 FROM "person" AS "t1") AS "_wrapped"', [])
而用 len()
方法執行的 SQL 語句倒是:
('SELECT "t1"."id", "t1"."Name", "t1"."Age", "t1"."Birthday", "t1"."Remarks" FROM "person" AS "t1"', [])
直接返回全部記錄而後獲取長度,這種方法是很是不可取的。
Person.select().order_by(Person.Age)
排序默認是升序排列,也能夠用 +
或 asc()
來明確表示是升序排列:
Person.select().order_by(+Person.Age)
Person.select().order_by(Person.Age.asc())
用 -
或 desc()
來表示降序:
Person.select().order_by(-Person.Age)
Person.select().order_by(Person.Age.desc())
如要對多個字段進行排序,逗號分隔寫就能夠了。
當查詢條件不止一個,須要使用邏輯運算符鏈接,而 Python 中的 and
、or
在 Peewee 中是不支持的,此時咱們須要使用 Peewee 封裝好的運算符,以下:
邏輯符 | 含義 | 樣例 |
---|---|---|
& | and | Person.select().where((Person.Name == '張三') & (Person.Age == 30)) |
| | or | Person.select().where((Person.Name == '張三') \| (Person.Age == 30)) |
~ | not | Person.select().where(~Person.Name == '張三') |
特別注意:有多個條件時,每一個條件必須用 () 括起來。
當條件全爲 and 時,也能夠用逗號分隔,get 和 select 中均可以:
Person.get(Person.Name == '張三', Person.Age == 30)
運算符 | 含義 |
---|---|
== | 等於 |
< | 小於 |
<= | 小於等於 |
> | 大於 |
>= | 大於等於 |
!= | 不等於 |
<< | x in y,其中 y 是列表或查詢 |
>> | x is y, 其中 y 能夠是 None |
% | x like y |
** | x like y |
注意:因爲 SQLite 的 LIKE 操做默認狀況下不區分大小寫,所以 peewee 將使用 SQLite GLOB 操做進行區分大小寫的搜索。glob 操做使用星號表示通配符,而不是一般的百分號。若是您正在使用 SQLite 並但願區分大小寫的部分字符串匹配,請記住使用星號做爲通配符。
解釋一下,在 SQLite 中,若是但願 like 的時候區分大小寫,能夠這麼寫:
Person.select().where(Person.Remarks % 'a*')
若是不但願區分大小寫,這麼寫:
Person.select().where(Person.Remarks ** 'a%')