純Python輕松開發(fā)在線留言板!

添加微信號"CNFeffery"加入技術(shù)交流群
?本文示例代碼已上傳至我的
?Github倉庫https://github.com/CNFeffery/DataScienceStudyNotes
1 簡介
這是我的系列教程「Python+Dash快速web應(yīng)用開發(fā)」的第十七期,在之前的各期教程中,我們針對Dash中各種基礎(chǔ)且常用的概念展開了學(xué)習(xí),但一直沒有針對與數(shù)據(jù)庫之間交互進行專門的介紹,只是在某些示例中利用pandas、SQLAlchemy等工具簡陋地操作數(shù)據(jù)庫。
而在今天的教程中,我就將帶大家學(xué)習(xí)在Dash中利用簡單好用的ORM庫peewee,快速高效地將數(shù)據(jù)庫整合進Dash應(yīng)用中。

2 利用peewee在Dash中整合數(shù)據(jù)庫
說起peewee,很多使用過ORM(Object Relational Mapping,對象關(guān)系映射)工具的朋友都聽說過,它跟SQLAlchemy等框架從功能上看都大同小異,目的都是為了「不寫SQL」,而是利用面向?qū)ο缶幊痰姆绞?,?code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">Python中實現(xiàn)常用的SQL功能。

peewee雖然相比SQLAlchemy等重型的ORM框架已經(jīng)輕量很多了,但內(nèi)容還是非常豐富,我們今天就針對一些典型場景,展示一下其與Dash應(yīng)用如何相互結(jié)合。
2.1 創(chuàng)建數(shù)據(jù)表
利用peewee構(gòu)建數(shù)據(jù)表,需要定義相應(yīng)的Model類,在類中構(gòu)建的屬性即對應(yīng)表中的字段,并且在Meta類中定義其他的一些屬性,譬如下面的例子我們就以最簡單的SQLite數(shù)據(jù)庫為例:
?model1.py
?
from peewee import SqliteDatabase, Model
from peewee import CharField, IntegerField, DateTimeField
from datetime import datetime
# 關(guān)聯(lián)數(shù)據(jù)庫,對于sqlite數(shù)據(jù)庫若不存在則會直接創(chuàng)建
db = SqliteDatabase('17 整合數(shù)據(jù)庫/model1.db')
class Model1(Model):
# 用戶名為字符型,并設(shè)置唯一性約束
username = CharField(unique=True)
# 用戶等級設(shè)定為整數(shù)型
level = IntegerField()
# 用戶加入時間為時間日期類型
join_datetime = DateTimeField()
class Meta:
database = db # 指定數(shù)據(jù)庫
table_name = 'user_info' # 自定義數(shù)據(jù)表名,不設(shè)置則自動根據(jù)類名推導(dǎo)
# 創(chuàng)建數(shù)據(jù)表,若對應(yīng)數(shù)據(jù)庫中已存在此表,則會跳過
db.create_tables([Model1])
上述的代碼在執(zhí)行之后,便會在關(guān)聯(lián)到的SQLite數(shù)據(jù)庫中創(chuàng)建對應(yīng)的表:

而除了最簡單的SQLite之外,peewee還支持MySQL、PostgreSQL,你可以在http://docs.peewee-orm.com/en/latest/peewee/database.html查看更多使用示例,關(guān)于更多有關(guān)Model創(chuàng)建的知識可以參考http://docs.peewee-orm.com/en/latest/peewee/models.html。
2.2 向表中新增記錄
在數(shù)據(jù)表創(chuàng)建完成之后,我們第一件事當(dāng)然是要向表中插入數(shù)據(jù),這在peewee中操作非常簡單:
「插入單條數(shù)據(jù)」
在peewee中向表中插入單條記錄可以使用create()方法:
# 創(chuàng)建單條記錄
Model1.create(username='張三', level=6, join_datetime=datetime(2020, 1, 1, 10, 28, 45))
Model1.create(username='李四', level=1, join_datetime=datetime(2020, 5, 1, 10, 28, 45))
執(zhí)行完上述命令后旋即會更新到數(shù)據(jù)庫表中:

「插入多條數(shù)據(jù)」
在peewee中批量插入數(shù)據(jù)可以使用insert_many()方法傳入對應(yīng)每行內(nèi)容的字典列表,記得最后要跟著執(zhí)行execute()方法才會真正向數(shù)據(jù)庫執(zhí)行:
# 批量插入數(shù)據(jù)
(
Model1
.insert_many([
{'username': '王五', 'level': 3, 'join_datetime': datetime(2020, 3, 1, 10, 28, 45)},
{'username': '趙六', 'level': 2, 'join_datetime': datetime(2020, 4, 1, 10, 28, 45)}])
.execute()
)

2.3 從表中刪除數(shù)據(jù)
對于已存在數(shù)據(jù)的表,進行數(shù)據(jù)刪除可以使用到delete()方法其后再鏈?zhǔn)缴?code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">where()來聲明判斷條件,最后同樣跟上execute()方法執(zhí)行即可,如果要清空整張表則不用加where(),譬如我們要刪除level小于3的記錄:
# 刪除level小于3的記錄
Model1.delete().where(Model1.level < 3).execute()

更多關(guān)于peewee數(shù)據(jù)刪除的知識可以參考官方文檔http://docs.peewee-orm.com/en/latest/peewee/querying.html#deleting-records部分內(nèi)容。
2.4 對表中數(shù)據(jù)進行更新
作為「增刪改查」中非常重要的「改」,在peewee中實現(xiàn)也是非常的方便,基礎(chǔ)的用法是配合update()與where()如下面的例子那樣:
# 修改username為張三的記錄值level字段為8
Model1.update(level=8).where(Model1.username == '張三').execute()

更多內(nèi)容可參考官方文檔http://docs.peewee-orm.com/en/latest/peewee/querying.html#updating-existing-records。
2.5 對表中數(shù)據(jù)進行查詢
作為「增刪改查」中使用頻次最高的「查」,在peewee中涉及到的知識內(nèi)容非常之龐大,但基礎(chǔ)的格式都是利用select()方法,常用的有以下方式:
# 獲取查詢結(jié)果方式1:
query_results = Model1.select().where(Model1.level > 2).execute()
for query_result in query_results:
print(query_result.username)

# 獲取查詢結(jié)果方式2:
query_results = Model1.select().where(Model1.level > 2).dicts()
list(query_results)

而有關(guān)跨表連接等進階的查詢操作,請參考官方文檔http://docs.peewee-orm.com/en/latest/peewee/query_examples.html#query-examples。
2.6 基于已存在的表逆向生成Model
如果你的數(shù)據(jù)庫表已然存在,又希望生成相應(yīng)的Model類,peewee提供了命令行工具幫我們做這件事,以SQLite為例:
python -m pwiz -e sqlite model1.db >model2.py
自動生成的model2.py代碼如下,在這個基礎(chǔ)上我們可以進一步的優(yōu)化修改:
from peewee import *
database = SqliteDatabase('model1.db')
class UnknownField(object):
def __init__(self, *_, **__): pass
class BaseModel(Model):
class Meta:
database = database
class UserInfo(BaseModel):
join_datetime = DateTimeField()
level = IntegerField()
username = CharField(unique=True)
class Meta:
table_name = 'user_info'
而更多關(guān)于peewee利用pwiz生成Model類的參數(shù)和用法可參考官方文檔http://docs.peewee-orm.com/en/latest/peewee/playhouse.html#pwiz-a-model-generator。
3 peewee配合Dash實現(xiàn)在線留言板功能
get到peewee的常用基礎(chǔ)用法之后,我們回到本文的重點——結(jié)合Dash整合數(shù)據(jù)庫,要實現(xiàn)的功能很簡單,就是實現(xiàn)一個在線留言板,每個訪問應(yīng)用的用戶都可以在填寫若干信息后,發(fā)表自己的留言,其他用戶后續(xù)訪問可以看到前面用戶發(fā)表過的留言信息。
為了方便演示,我選擇SQLite作為示例數(shù)據(jù)庫,首先我們需要構(gòu)建一個model.py來設(shè)計表模型,來存放每條留言信息,并自定義一些功能函數(shù):
?model.py
?
from peewee import SqliteDatabase, Model
from peewee import CharField, DateTimeField, TextField
from datetime import datetime
db = SqliteDatabase('17 整合數(shù)據(jù)庫/message_board.db')
class MessageBoard(Model):
nickname = CharField()
pub_datetime = DateTimeField()
message_content = TextField()
class Meta:
database = db # 指定數(shù)據(jù)庫
table_name = 'message_board' # 自定義數(shù)據(jù)表名,不設(shè)置則自動根據(jù)類名推導(dǎo)
db.create_tables([MessageBoard])
# 新增留言記錄
def submit_new_message(nickname, message_content):
MessageBoard.create(
nickname=nickname,
pub_datetime=datetime.now(),
message_content=message_content
)
# 獲取全部留言記錄
def fetch_all_message():
return list(MessageBoard.select().dicts())
接著我們只需要在對應(yīng)Dash應(yīng)用的app.py中調(diào)用model.py中的相關(guān)功能即可,效果如下(動圖錄制有些花屏,大家可以自己運行嘗試,效果更佳):

?app.py
?
import dash
import dash_html_components as html
import dash_bootstrap_components as dbc
from dash.dependencies import Input, Output, State
from model import MessageBoard, submit_new_message, fetch_all_message
app = dash.Dash(__name__)
app.layout = html.Div(
dbc.Container(
[
html.Div(style={'height': '20px'}),
html.H2('Dash示例留言板'),
dbc.Container(
id='history-message',
style={
'paddingTop': '50px',
'width': '70%',
'height': '70%',
'overflowY': 'auto',
'backgroundColor': '#fafafa'
}
),
dbc.Container(
dbc.Row(
[
dbc.Col(
dbc.Input(placeholder='輸入昵稱:', id='nickname', style={'width': '100%'}),
width=3,
style={
'padding': 0
}
),
dbc.Col(
dbc.Input(placeholder='輸入留言內(nèi)容:', id='message', style={'width': '100%'}),
width=7,
style={
'padding': 0
}
),
dbc.Col(
dbc.Button('提交', id='submit', color='primary', block=True),
width=2,
style={
'padding': 0
}
)
]
),
style={
'paddingTop': '10px',
'width': '70%',
}
)
],
style={
'height': '800px',
'boxShadow': 'rgb(0 0 0 / 20%) 0px 13px 30px, rgb(255 255 255 / 80%) 0px -13px 30px',
'borderRadius': '10px'
}
),
style={
'paddingTop': '50px'
}
)
@app.callback(
Output('history-message', 'children'),
Input('submit', 'n_clicks'),
[State('nickname', 'value'),
State('message', 'value')]
)
def refresh_message_board(n_clicks, nickname, message):
if nickname and message:
submit_new_message(nickname, message)
return [
html.Div(
[
html.Strong(record['nickname']),
html.Span(' '),
html.Em(record['pub_datetime'].strftime(format='%Y-%m-%d %H:%M:%S')),
html.Br(),
html.P(record['message_content'])
]
)
for record in fetch_all_message()
]
if __name__ == '__main__':
app.run_server(debug=True)
有關(guān)peewee的內(nèi)容非常豐富,想要完全記住不太現(xiàn)實,大家可以養(yǎng)成多查官網(wǎng)http://docs.peewee-orm.com/en/latest/的習(xí)慣,內(nèi)容非常詳細(xì)生動,給官方點個贊!
以上就是本文的全部內(nèi)容,歡迎在評論區(qū)發(fā)表你的意見和想法。

加入知識星球【我們談?wù)摂?shù)據(jù)科學(xué)】
400+小伙伴一起學(xué)習(xí)!
· 推薦閱讀 ·
