[TOC]
最近嘗試使用tornado。由于tornado組件較為簡潔履磨,還需引入一些其他類庫結合使用。在ORM方面庆尘,我選擇了輕量小巧的peewee剃诅。本文即對peewee的用法進行初步考察,記錄自用驶忌。
主要參考內容:
peewee官方文檔
考察目標
peewee支持多種數據庫矛辕,而本文的考察環(huán)境是mysql,存儲引擎為InnoDB付魔。
常用的sql操作可簡單分為CURD聊品。我平常工作使用的功能細分如下:
- Create:單條插入、批量插入几苍、批量塊插入翻屈、獲取自增pk
- Update:數據更新
- Retrieve:單條查詢、多條查詢妻坝、指定字段單條/多條查詢伸眶、關聯(lián)查詢(join、union)刽宪、聚合查詢
- Delete:單條刪除厘贼、批量刪除
- 其他功能:事務、原生sql查詢
前置準備
在mysql中新建表
本文使用數據庫名為peewee纠屋,三張表分別是user涂臣、balance和integral,結構如下:
CREATE TABLE `user` (
`uid` int(11) NOT NULL AUTO_INCREMENT COMMENT '用戶編號',
`avartar` varchar(200) NOT NULL COMMENT '頭像',
`uname` varchar(50) NOT NULL COMMENT '用戶名',
`gender` char(1) NOT NULL DEFAULT '0' COMMENT '性別 0-保密|1-男|2-女',
`password` varchar(200) NOT NULL COMMENT '密碼',
`balance` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '余額',
`integral` int(11) NOT NULL DEFAULT '0' COMMENT '積分',
`logintime` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '最后登錄時間',
`addtime` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '注冊時間',
PRIMARY KEY (`uid`),
UNIQUE KEY `uname` (`uname`) COMMENT '用戶名唯一'
) ENGINE=InnoDB AUTO_INCREMENT=10000 DEFAULT CHARSET=utf8 COMMENT='用戶信息'
CREATE TABLE `balance` (
`b_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '操作編號',
`uid` int(11) NOT NULL DEFAULT '0' COMMENT '用戶編號',
`change` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '變動金額',
`total` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '剩余金額',
`addtime` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '操作時間',
PRIMARY KEY (`b_id`),
KEY `uid` (`uid`) COMMENT '用戶編號'
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='余額操作記錄表'
CREATE TABLE `integral` (
`i_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '操作編號',
`uid` int(11) NOT NULL DEFAULT '0' COMMENT '用戶編號',
`change` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '變動積分',
`total` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '剩余積分',
`addtime` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '操作時間',
PRIMARY KEY (`i_id`),
KEY `uid` (`uid`) COMMENT '用戶編號'
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='積分操作記錄表'
python連接mysql售担,需要pymysql或mysql-connector-python赁遗。
pip install pymysql peewee
生成映射模型
peewee分別提供根據模型生成表和根據表生成模型的功能。但peewee等orm要考慮兼容多種數據庫族铆,因此模型生成表的過程中只能使用所有數據庫公有的字段類型岩四,不能使用mysql的tinyint、char等類型哥攘,所以不考慮根據模型生成表剖煌。使用pwiz命令生成模型:
python -m pwiz -e mysql -H localhost -p3306 -uroot -P peewee > models.py
獲得以下model文件:
# -*- coding: utf-8 -*-
# models.py
from peewee import *
database = MySQLDatabase('peewee', **{'charset': 'utf8', 'use_unicode': True, 'host': 'localhost', 'port': 3306, 'user': 'root', 'password': ''})
class UnknownField(object):
def __init__(self, *_, **__): pass
class BaseModel(Model):
class Meta:
database = database
class Balance(BaseModel):
addtime = DateTimeField(constraints=[SQL("DEFAULT CURRENT_TIMESTAMP")])
b = AutoField(column_name='b_id')
change = DecimalField(constraints=[SQL("DEFAULT 0.00")])
total = DecimalField(constraints=[SQL("DEFAULT 0.00")])
uid = IntegerField(constraints=[SQL("DEFAULT 0")], index=True)
class Meta:
table_name = 'balance'
class Integral(BaseModel):
addtime = DateTimeField(constraints=[SQL("DEFAULT CURRENT_TIMESTAMP")])
change = DecimalField(constraints=[SQL("DEFAULT 0.00")])
i = AutoField(column_name='i_id')
total = DecimalField(constraints=[SQL("DEFAULT 0.00")])
uid = IntegerField(constraints=[SQL("DEFAULT 0")], index=True)
class Meta:
table_name = 'integral'
class User(BaseModel):
addtime = DateTimeField(constraints=[SQL("DEFAULT CURRENT_TIMESTAMP")])
avartar = CharField()
balance = DecimalField(constraints=[SQL("DEFAULT 0.00")])
gender = CharField(constraints=[SQL("DEFAULT '0'")])
integral = IntegerField(constraints=[SQL("DEFAULT 0")])
logintime = DateTimeField(constraints=[SQL("DEFAULT CURRENT_TIMESTAMP")])
password = CharField()
uid = AutoField()
uname = CharField(unique=True)
class Meta:
table_name = 'user'
Create
新建一個peewee_test.py文件材鹦,引入models,使用logging類庫觀察執(zhí)行的sql語句耕姊。
第一種添加方法
# -*- coding: utf-8 -*-
# peewee_test.py
from models import *
import sys, logging, hashlib
# 使用日志觀察執(zhí)行的sql語句
logger = logging.getLogger('peewee')
logger.setLevel(logging.DEBUG)
logger.addHandler(logging.StreamHandler())
def create():
"""md5加密密碼"""
m = hashlib.md5()
m.update('123456'.encode())
pwd = m.hexdigest()
"""頭像"""
avartar = 'https://avatars2.githubusercontent.com/u/25029451'
"""第一種添加方法"""
user = User(
avartar=avartar,
uname='John', gender='1', password=pwd
)
uid = user.save()
print('uid=%d' % uid)
if __name__ == '__main__':
sys.exit(create())
運行peewee_test.py桶唐,查看控制臺輸出信息,我們得知第一種添加方法不會返回插入的自增pk(此時自增pk為uid=10000)茉兰,而是成功返回1尤泽,失敗返回0;
python peewee_test.py
('INSERT INTO `user` (`avartar`, `gender`, `password`, `uname`) VALUES (%s, %s, %s, %s)', ['https://avatars2.githubusercontent.com/u/25029451', '1', 'e10adc3949ba59abbe56e057f20f883e', 'John'])
uid=1
Process finished with exit code 0
第二種添加方法
def create():
"""第二種添加方法"""
uid = User.create(
avartar=avartar,
uname='Jack', gender='1', password=pwd
)
print('uid=%s' % uid)
注意第二種方法中我打印uid使用格式化操作符是%s而不是%d规脸,因為返回值是一個User對象
查看控制臺輸出信息坯约,可知第二種方法會返回自增pk;
python peewee_test.py
('INSERT INTO `user` (`avartar`, `gender`, `password`, `uname`) VALUES (%s, %s, %s, %s)', ['https://avatars2.githubusercontent.com/u/25029451', '1', 'e10adc3949ba59abbe56e057f20f883e', 'Jack'])
uid=10001
Process finished with exit code 0
第三種添加方法
def create():
"""第三種添加方法"""
uid = User.insert(
avartar=avartar,
uname='Micheal', gender='1', password=pwd
).execute()
print('uid=%d' % uid)
查看控制臺輸出信息莫鸭,第三種方法也會返回自增pk闹丐。
python peewee_test.py
('INSERT INTO `user` (`avartar`, `gender`, `password`, `uname`) VALUES (%s, %s, %s, %s)', ['https://avatars2.githubusercontent.com/u/25029451', '1', 'e10adc3949ba59abbe56e057f20f883e', 'Micheal'])
uid=10002
Process finished with exit code 0
第一種需要調用save()方法才能插入數據,而第二種不需要調用其他方法被因,第三種則調用execute()方法進行插入卿拴。第二種方法返回的是對象,第三種方法返回的是整型梨与。工作中我經常需要在后續(xù)代碼中使用到自增pk巍棱,因此我將采用insert()方法進行添加操作。
批量添加方法
使用insert_many批量添加數據:在插入多條數據時蛋欣,根據文檔描述航徙,create()方法每次都會開啟事務(如果存儲引擎支持),雖然可以遍歷方式用上述方法逐條插入陷虎,但數據量一大就會導致效率低下到踏,所以有必要進行批量插入優(yōu)化。
If you are not wrapping the loop in a transaction then each call to create() happens in its own transaction. That is going to be really slow!
def create():
"""批量添加數據"""
data_source = [
(avartar, 'Catherine', '2', pwd),
(avartar, 'Jane', '2', pwd),
(avartar 'Mary', '2', pwd),
]
field = [User.avartar, User.uname, User.gender, User.password]
uid = User.insert_many(data_source, field).execute()
print('uid=%d' % uid)
控制臺輸出返回批量插入過程中第一條數據的自增pk尚猿;
python peewee_test.py
('INSERT INTO `user` (`avartar`, `uname`, `gender`, `password`) VALUES (%s, %s, %s, %s), (%s, %s, %s, %s), (%s, %s, %s, %s)', ['https://avatars2.githubusercontent.com/u/25029451', 'Catherine', '2', 'e10adc3949ba59abbe56e057f20f883e', 'https://avatars2.githubusercontent.com/u/25029451', 'Jane', '2', 'e10adc3949ba59abbe56e057f20f883e', 'https://avatars2.githubusercontent.com/u/25029451', 'Mary', '2', 'e10adc3949ba59abbe56e057f20f883e'])
uid=10003
Process finished with exit code 0
批量塊添加方法
如果要插入的數據量非常大窝稿,比如有千萬條。一次性批量插入將會導致卡死凿掂,逐條插入也會導致耗時過長伴榔。此時可以折中尋求一個分批次批量插入的方案:使用peewee的chunked(it, n)方法將數據分割成小塊數據(比如每次1000條,chunked(data_source, 1000))分批次批量插入:
def create():
"""批量塊添加數據"""
data_source = [
(avartar, 'Zoe', '2', pwd),
(avartar, 'Lucy', '2', pwd),
(avartar, 'Kara', '2', pwd),
(avartar, 'Rex', '1', pwd),
]
field = [User.avartar, User.uname, User.gender, User.password]
for data_chunk in chunked(data_source, 2):
User.insert_many(data_chunk, field).execute()
控制臺輸出的信息表明了chunked的實質:
python peewee_test.py
('INSERT INTO `user` (`avartar`, `uname`, `gender`, `password`) VALUES (%s, %s, %s, %s), (%s, %s, %s, %s)', ['https://avatars2.githubusercontent.com/u/25029451', 'Zoe', '2', 'e10adc3949ba59abbe56e057f20f883e', 'https://avatars2.githubusercontent.com/u/25029451', 'Lucy', '2', 'e10adc3949ba59abbe56e057f20f883e'])
('INSERT INTO `user` (`avartar`, `uname`, `gender`, `password`) VALUES (%s, %s, %s, %s), (%s, %s, %s, %s)', ['https://avatars2.githubusercontent.com/u/25029451', 'Kara', '2', 'e10adc3949ba59abbe56e057f20f883e', 'https://avatars2.githubusercontent.com/u/25029451', 'Rex', '1', 'e10adc3949ba59abbe56e057f20f883e'])
Process finished with exit code 0
Retrieve(1)
讀取單條數據
直接調用get()方法庄萎,可以添加過濾(filter)條件踪少。查詢結果是一個User對象,直接打印得到pk糠涛,通過調用成員變量可以獲得對應字段的值援奢。特別的,當篩選條件為pk時忍捡,可以使用get_by_id()方法直接查詢:
def retrieve():
"""讀取一條數據"""
# result = User.get_by_id(10000)
result = User.get(User.uname == 'Jack')
print('直接打蛹:%s' % result)
"""查看結構"""
print('\n'.join(['%s:%s' % item for item in result.__data__.items()]))
print('用戶名為:%s' % result.uname)
控制臺輸出:
python peewee_test.py
('SELECT `t1`.`uid`, `t1`.`addtime`, `t1`.`avartar`, `t1`.`balance`, `t1`.`gender`, `t1`.`integral`, `t1`.`logintime`, `t1`.`password`, `t1`.`uname` FROM `user` AS `t1` WHERE (`t1`.`uname` = %s) LIMIT %s OFFSET %s', ['Jack', 1, 0])
直接打忧星:10001
uid:10001
addtime:2018-12-12 10:22:53
avartar:https://avatars2.githubusercontent.com/u/25029451
balance:0.00
gender:1
integral:0
logintime:2018-12-12 10:22:53
password:e10adc3949ba59abbe56e057f20f883e
uname:Jack
用戶名為:Jack
Process finished with exit code 0
使用get()方法查詢出來的數據包含了所有的字段。那么具篇,當我們的需求是查詢單條數據中的限定字段(fields)時纬霞,應該怎么查詢呢?閱讀get()方法的源碼可以得知驱显,其調用了select()方法返回的ModelSelect類中的get()方法:
# peewee.py
@classmethod
def get(cls, *query, **filters):
sq = cls.select()
if query:
sq = sq.where(*query)
if filters:
sq = sq.filter(**filters)
return sq.get()
因此险领,我們可以直接調用select()方法,然后調用get()方法秒紧,就可以實現需求。
def retrieve():
"""查詢一條數據中的限定字段(uname)"""
result = User.select(User.uname).where(User.uid == 10001).get()
"""查看結構"""
print('\n'.join(['%s:%s' % item for item in result.__data__.items()]))
事實上挨下,很多時候工作中遇到的單一查詢都是只需要其中幾個字段的數據熔恢,因此比起get()我用的更多的將會是select().get()。
python peewee_test.py
('SELECT `t1`.`uname` FROM `user` AS `t1` WHERE (`t1`.`uid` = %s) LIMIT %s OFFSET %s', [10001, 1, 0])
uname:Jack
Process finished with exit code 0
讀取多條數據
讀取多條數據時臭笆,就要用到剛才提到的select()方法叙淌,這也是查詢使用的主要方法。查閱select()方法源碼可知愁铺,其返回一個ModelSelect類鹰霍,然后我們再調用這個類的各種方法(where()、order_by()茵乱、limit()茂洒、offset())來實現查詢的條件限定。如果你有特殊的需求瓶竭,可以在鏈式操作的最后調用dicts()方法督勺,將結果轉化為一個字典。例如斤贰,我們要查詢性別為男性智哀、按注冊日期降序(升序寫字段,降序在字段前面再加個負號)排列的第二和第三位用戶的編號和名稱:
def retrieve():
"""讀取多條數據"""
query = User.select(User.uname, User.uid).order_by(-User.addtime).limit(2).offset(1).where(User.gender == '1')
for row in query:
print('%d:%s' % (row.uid, row.uname))
"""轉換為字典形式"""
print('轉為字典形式:')
query = User.select(User.uname, User.uid).order_by(-User.addtime).limit(2).offset(1).where(User.gender == '1').dicts()
for row in query:
print(row)
python peewee_test.py
('SELECT `t1`.`uname`, `t1`.`uid` FROM `user` AS `t1` WHERE (`t1`.`gender` = %s) ORDER BY `t1`.`addtime` DESC LIMIT %s OFFSET %s', ['1', 2, 1])
('SELECT `t1`.`uname`, `t1`.`uid` FROM `user` AS `t1` WHERE (`t1`.`gender` = %s) ORDER BY `t1`.`addtime` DESC LIMIT %s OFFSET %s', ['1', 2, 1])
10002:Micheal
10001:Jack
轉為字典形式:
{'uname': 'Micheal', 'uid': 10002}
{'uname': 'Jack', 'uid': 10001}
Process finished with exit code 0
查詢表達式
根據文檔荧恍,peewee的查詢操作符由以下組成:
比較符號 | 意義 |
---|---|
== | x等于y |
< | x小于y |
<= | x小于等于y |
> | x大于y |
>= | x大于等于 |
!= | x不等于y |
<< | x在數組y中 |
>> | x是y瓷叫,這個y是None或者Null |
% | x包含在y中 |
** | 忽略大小寫的x包含在y中 |
^ | x異或y |
~ | 非x |
舉例,模糊查詢用戶名包含J的用戶:
def retrieve():
"""模糊查詢用戶名包含J的用戶"""
# 包含J結尾的查詢條件
# query = User.select(User.uname, User.uid).where(User.uname % '%J').dicts()
# 包含J的查詢條件
# query = User.select(User.uname, User.uid).where(User.uname % '%J%').dicts()
# 包含J開頭的查詢條件
query = User.select(User.uname, User.uid).where(User.uname % 'J%').dicts()
for row in query:
print(row)
我們應該知道送巡,模糊查詢的時候使用右模糊查詢可以有效地利用索引加快查詢速度摹菠。
python peewee_test.py
('SELECT `t1`.`uname`, `t1`.`uid` FROM `user` AS `t1` WHERE (`t1`.`uname` LIKE BINARY %s)', ['J%'])
{'uname': 'Jack', 'uid': 10001}
{'uname': 'Jane', 'uid': 10004}
{'uname': 'John', 'uid': 10000}
Process finished with exit code 0
當然,因為操作符有限骗爆,無法表達全部功能辨嗽,所以peewee同時也提供了方法用于代替操作符:
方法 | 意義 |
---|---|
.in_(value) | x在數組y中 |
.not_in(value | x不在數組y中 |
.is_null(is_null) | 判斷是否為空,接受布爾值 |
.contains(substr) | 包含字符串淮腾,忽略大小寫 |
.startswith(prefix) | 以字符串開頭糟需,忽略大小寫 |
.endswith(suffix) | 以字符串結尾屉佳,忽略大小寫 |
.between(low, high) | 在兩個值的范圍內 |
.regexp(exp) | 匹配正則表達式(大小寫敏感) |
.iregexp(exp) | 匹配正則表達式,忽略大小寫 |
.bin_and(value) | 二進制的和 |
.bin_or(value) | 二進制的或 |
.concat(other) | 將兩個字符串或對象臨時合并 |
.distinct() | 指定字段過濾重復記錄 |
.collate(collation) | 對列進行指定規(guī)則排序 |
.cast(type) | 將列的值按照指定類型轉變 |
還是上面的查詢例子洲押,等效用方法進行查詢可改為:
# 包含J結尾的查詢條件
# query = User.select(User.uname, User.uid).where(User.uname.endswith('j')).dicts()
# 包含J的查詢條件
# query = User.select(User.uname, User.uid).where(User.uname.contains('j')).dicts()
# 包含J開頭的查詢條件
query = User.select(User.uname, User.uid).where(User.uname.startswith('j')).dicts()
以及連接多個條件的邏輯運算符:
符號 | 意義 |
---|---|
& | 且 |
| | 或 |
~ | 非 |
Update
第一種更新方法
和添加操作一樣武花,更新也有多種方法。當條件中包含了pk時杈帐,調用save()方法就是更新:
def update():
"""第一種更新方法"""
user = User(gender='1')
user.uid = 1006
user.save()
python peewee_test.py
('UPDATE `user` SET `gender` = %s WHERE (`uid` = %s)', ['1', 1006])
Process finished with exit code 0
第二種更新方法
同insert()体箕,改成update()就可以了,比第一種方法優(yōu)越在于可以使用其他字段進行條件限定:
def update():
"""第二種更新方法"""
User.update(
gender='2'
).where(User.uname == 'Zoe').execute()
python peewee_test.py
('UPDATE `user` SET `gender` = %s WHERE (`uname` = %s)', ['2', 'Zoe'])
Process finished with exit code 0
Transaction
掌握了最基本的CUR操作后挑童,我們終于可以進行一些更為高級的操作累铅。比如很重要很常用并且是InnoDB比MyISAM更為優(yōu)越的功能:事務操作。peewee提供了兩種事務開啟的方法站叼,這里只用第二種娃兽。
首先,我們可以在models.py這個文件的BaseModel類中添加一個trans()方法尽楔,便于后面開啟事務:
# models.py
class BaseModel(Model):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.trans = database.atomic
class Meta:
database = database
開啟事務的方法
def transaction():
user = User()
"""開啟事務"""
with user.trans():
pass
在python中使用with開啟的好處在于如果原子操作執(zhí)行成功它可以自動提交投储、失敗會自動回滾,不需要程序員手寫提交和回滾代碼阔馋,除此之外玛荞,視覺上更為優(yōu)雅(pythonic)。當然如果你更習慣于完全手寫也可以使用database.manual_commit()的方法來開啟手動提交回滾操作呕寝。
應用情景
給名為Jack的用戶添加100.00余額勋眯,同時需要在余額操作記錄表上插入一條記錄。兩個操作必須全部成功下梢,否則全部失敗凡恍。
def transaction():
user = User()
result = user.select(User.uid, User.balance).where(User.uname == 'Jack').get() # 查詢用戶的現有余額以及用戶編號
"""開啟事務"""
with user.trans():
try:
user.update(
balance=result.balance+100
).where(User.uid == result.uid).execute()
Balance.insert(
uid=result.uid,
change=100,
total=result.balance+100
).execute()
except Exception:
print('更新失敗')
else:
print('更新成功')
如果其中有一步操作失敗,則整個事務自動回滾怔球。
python peewee_test.py
('SELECT `t1`.`uid`, `t1`.`balance` FROM `user` AS `t1` WHERE (`t1`.`uname` = %s) LIMIT %s OFFSET %s', ['Jack', 1, 0])
('UPDATE `user` SET `balance` = %s WHERE (`uid` = %s)', [Decimal('100.00'), 10001])
('INSERT INTO `balance` (`change`, `total`, `uid`) VALUES (%s, %s, %s)', [100, Decimal('100.00'), 10001])
更新成功
Process finished with exit code 0
Retrieve(2)
join
當我們查看操作記錄的時候嚼酝,往往需要用戶信息和操作記錄組合起來,也就是mysql的join操作竟坛。例如闽巩,我們要時間降序查詢用戶編號為10001的用戶的余額變更記錄:
def retrieve2():
"""join查詢"""
query = Balance.select(
User.uname, Balance.change,
Balance.total, Balance.addtime
).join(
User, JOIN.RIGHT_OUTER,
on=(Balance.uid == User.uid)
).where(
Balance.uid == 10001
).order_by(-Balance.addtime)
for row in query:
print('%s:%.2f:%.2f:%s' % (
row.user.uname, row.change, row.total, row.addtime
))
python peewee_test.py
('SELECT `t1`.`uname`, `t2`.`change`, `t2`.`total`, `t2`.`addtime` FROM `balance` AS `t2` RIGHT OUTER JOIN `user` AS `t1` ON (`t1`.`uid` = `t2`.`uid`) WHERE (`t2`.`uid` = %s) ORDER BY `t2`.`addtime` DESC', [10001])
Jack:100.00:200.00:2018-12-12 17:24:32
Jack:100.00:100.00:2018-12-12 16:31:15
Process finished with exit code 0
union[ all]
關于union的操作姿勢,官方文檔也提供了多種方式担汤,包括操作符涎跨、方法等等。
操作符 | 意義 |
---|---|
| | union |
+ | union all |
當我們需要把余額操作記錄和積分操作記錄并作操作記錄統(tǒng)計時間降序查看第二和第三條數據(盡管這意義不大崭歧,且實際上我本來是想用訂單統(tǒng)計來作為示例)的時候隅很,union|union all就可以派上用場:
def retrieve2():
"""union_all查詢"""
b_query = (Balance.select(
Balance.b.alias('id'), Balance.change,
Balance.total, Balance.addtime
))
i_query = (Integral.select(
Integral.i.alias('id'), Integral.change,
Integral.total, Integral.addtime
))
query = (b_query + i_query).order_by(SQL('addtime desc')).limit(2).offset(1)
# query = b_query.union_all(i_query).order_by(SQL('addtime desc')).limit(2).offset(1)
for row in query:
print('%d:%.2f:%.2f:%s' % (row.id, row.change, row.total, row.addtime))
沒錯,union_all的操作看起來仿佛將兩個元組連接一樣(實際上源碼 __add__ = union_all)率碾。如果對此感到不習慣叔营,也可以用方法union_all()來代替屋彪。值得注意的是,在方法order_by()中绒尊,不能直接寫入'addtime desc'這樣的字符串畜挥,因為peewee不會將其解析為sql語句。你需要使用SQL類將其轉化為sql語句才能使排序生效(參考自StackOverflow)婴谱。
python peewee_test.py
('SELECT `t1`.`b_id` AS `id`, `t1`.`change`, `t1`.`total`, `t1`.`addtime` FROM `balance` AS `t1` UNION ALL SELECT `t2`.`i_id` AS `id`, `t2`.`change`, `t2`.`total`, `t2`.`addtime` FROM `integral` AS `t2` ORDER BY addtime desc LIMIT %s OFFSET %s', [2, 1])
1:300.00:300.00:2018-12-13 10:16:27
4:100.00:200.00:2018-12-12 17:24:32
Process finished with exit code 0
聚合查詢
聚合查詢包括count蟹但、max、min谭羔、avg华糖、sum。
count
def count():
"""統(tǒng)計用戶個數"""
count = User.select().count()
print('用戶數量:%d' % count)
"""fn.COUNT用法"""
fn_count = User.select(fn.COUNT(User.uid)).scalar()
print('fn.COUNT:%d' % fn_count)
max
def max():
fn_max = User.select(fn.MAX(User.balance)).scalar()
min
def min():
fn_min = User.select(fn.MIN(User.balance)).scalar()
avg
def avg():
fn_avg = User.select(fn.AVG(User.balance)).scalar()
sum
def sum():
fn_sum = Balance.select(fn.SUM(Balance.change)).where(Balance.uid == 10001).scalar()
Delete
刪除調用delete()方法瘟裸,使用execute()執(zhí)行
def delete():
result = User.delete().where(User.uid == 10009).execute()
print(result)
result2 = User.delete().where(User.uid << (10006, 10007, 10008, 10009)).execute()
print(result2)
返回值為成功刪除的數據條數客叉。
原生sql支持
使用pwiz生成models.py文件中,儲存著一個類MySQLDatabase景描,包含了數據庫連接的信息。這個類有一個cursor()方法秀撇,可以用來執(zhí)行原生sql語句(如果你的peewee版本低于3.0超棺,這個方法名是get_cursor())。
在models.py中的BaseModel添加self.cursor = database.cursor
簡單查詢示例
def sql():
user = User()
cursor = user.cursor()
cursor.execute('SELECT `uid`, `uname`, `gender` FROM `user` WHERE `uid`=10001')
for (uid, uname, gender) in cursor:
print('%d:%s:%s' % (uid, uname, gender))
批量插入使用executemany()呵燕,參考自CSDN棠绘。
在models.py中的BaseModel添加self.commit = database.commit
def sql():
"""執(zhí)行批量插入"""
user = User()
cursor = user.cursor()
"""md5密碼"""
m = hashlib.md5()
m.update('123456'.encode())
pwd = m.hexdigest()
"""頭像"""
avartar = 'https://avatars2.githubusercontent.com/u/25029451'
data_source = [
(avartar, 'Rem', pwd),
(avartar, 'Cosmos', pwd)
]
result = cursor.executemany('insert into `user`(`avartar`, `uname`, `password`) values (%s, %s, %s) ', data_source)
print(result) # 返回插入條數
user.commit() # 需要手動提交事務插入才能成功
cursor.close()
總結
文中記錄了我目前能想起來的工作中使用頻率較高的一些操作。其他的操作由于沒想起來便沒作記錄再扭,也許日后會更新加入氧苍。除此之外,鎖泛范、隔離級別让虐、分布式支持在peewee中的體現我暫時沒有什么頭緒,待google看看罢荡。關于鎖赡突,官方文檔中提到基本上都是sqlite相關的,還有一個樂觀鎖的例子区赵,但我粗略看了一下需要在表中添加version字段惭缰,所以沒有采用。