本文示例程式碼已上傳至我的
Github
倉庫https://github.com/CNFeffery/DataScienceStudyNotes
1 簡介
這是我的系列教程Python+Dash快速web應用開發的第十七期,在之前的各期教程中,我們針對Dash
中各種基礎且常用的概念展開了學習,但一直沒有針對與資料庫之間互動進行專門的介紹,只是在某些示例中利用pandas
、SQLAlchemy
等工具簡陋地運算元據庫。
而在今天的教程中,我就將帶大家學習在Dash
中利用簡單好用的ORM
庫peewee
,快速高效地將資料庫整合進Dash
應用中。
2 利用peewee在Dash中整合資料庫
說起peewee
,很多使用過ORM(Object Relational Mapping,物件關係對映)工具的朋友都聽說過,它跟SQLAlchemy
等框架從功能上看都大同小異,目的都是為了不寫SQL,而是利用物件導向程式設計的方式,在Python
中實現常用的SQL
功能。
peewee
雖然相比SQLAlchemy
等重型的ORM
框架已經輕量很多了,但內容還是非常豐富,我們今天就針對一些典型場景,展示一下其與Dash
應用如何相互結合。
2.1 建立資料表
利用peewee
構建資料表,需要定義相應的Model
類,在類中構建的屬性即對應表中的欄位,並且在Meta
類中定義其他的一些屬性,譬如下面的例子我們就以最簡單的SQLite
資料庫為例:
model1.py
from peewee import SqliteDatabase, Model
from peewee import CharField, IntegerField, DateTimeField
from datetime import datetime
# 關聯資料庫,對於sqlite資料庫若不存在則會直接建立
db = SqliteDatabase('17 整合資料庫/model1.db')
class Model1(Model):
# 使用者名稱為字元型,並設定唯一性約束
username = CharField(unique=True)
# 使用者等級設定為整數型
level = IntegerField()
# 使用者加入時間為時間日期型別
join_datetime = DateTimeField()
class Meta:
database = db # 指定資料庫
table_name = 'user_info' # 自定義資料表名,不設定則自動根據類名推導
# 建立資料表,若對應資料庫中已存在此表,則會跳過
db.create_tables([Model1])
上述的程式碼在執行之後,便會在關聯到的SQLite
資料庫中建立對應的表:
而除了最簡單的SQLite
之外,peewee
還支援MySQL
、PostgreSQL
,你可以在http://docs.peewee-orm.com/en/latest/peewee/database.html
檢視更多使用示例,關於更多有關Model
建立的知識可以參考http://docs.peewee-orm.com/en/latest/peewee/models.html
。
2.2 向表中新增記錄
在資料表建立完成之後,我們第一件事當然是要向表中插入資料,這在peewee
中操作非常簡單:
- 插入單條資料
在peewee
中向表中插入單條記錄可以使用create()
方法:
# 建立單條記錄
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))
執行完上述命令後旋即會更新到資料庫表中:
- 插入多條資料
在peewee
中批量插入資料可以使用insert_many()
方法傳入對應每行內容的字典列表,記得最後要跟著執行execute()
方法才會真正向資料庫執行:
# 批量插入資料
(
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 從表中刪除資料
對於已存在資料的表,進行資料刪除可以使用到delete()
方法其後再鏈式上where()
來宣告判斷條件,最後同樣跟上execute()
方法執行即可,如果要清空整張表則不用加where()
,譬如我們要刪除level
小於3的記錄:
# 刪除level小於3的記錄
Model1.delete().where(Model1.level < 3).execute()
更多關於peewee
資料刪除的知識可以參考官方文件http://docs.peewee-orm.com/en/latest/peewee/querying.html#deleting-records
部分內容。
2.4 對錶中資料進行更新
作為增刪改查中非常重要的改,在peewee
中實現也是非常的方便,基礎的用法是配合update()
與where()
如下面的例子那樣:
# 修改username為張三的記錄值level欄位為8
Model1.update(level=8).where(Model1.username == '張三').execute()
更多內容可參考官方文件http://docs.peewee-orm.com/en/latest/peewee/querying.html#updating-existing-records
。
2.5 對錶中資料進行查詢
作為增刪改查中使用頻次最高的查,在peewee
中涉及到的知識內容非常之龐大,但基礎的格式都是利用select()
方法,常用的有以下方式:
# 獲取查詢結果方式1:
query_results = Model1.select().where(Model1.level > 2).execute()
for query_result in query_results:
print(query_result.username)
# 獲取查詢結果方式2:
query_results = Model1.select().where(Model1.level > 2).dicts()
list(query_results)
而有關跨表連線等進階的查詢操作,請參考官方文件http://docs.peewee-orm.com/en/latest/peewee/query_examples.html#query-examples
。
2.6 基於已存在的表逆向生成Model
如果你的資料庫表已然存在,又希望生成相應的Model
類,peewee
提供了命令列工具幫我們做這件事,以SQLite
為例:
python -m pwiz -e sqlite model1.db >model2.py
自動生成的model2.py
程式碼如下,在這個基礎上我們可以進一步的優化修改:
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'
而更多關於peewee
利用pwiz
生成Model
類的引數和用法可參考官方文件http://docs.peewee-orm.com/en/latest/peewee/playhouse.html#pwiz-a-model-generator
。
3 peewee配合Dash實現線上留言板功能
get
到peewee
的常用基礎用法之後,我們回到本文的重點——結合Dash
整合資料庫,要實現的功能很簡單,就是實現一個線上留言板,每個訪問應用的使用者都可以在填寫若干資訊後,發表自己的留言,其他使用者後續訪問可以看到前面使用者發表過的留言資訊。
為了方便演示,我選擇SQLite
作為示例資料庫,首先我們需要構建一個model.py
來設計表模型,來存放每條留言資訊,並自定義一些功能函式:
model.py
from peewee import SqliteDatabase, Model
from peewee import CharField, DateTimeField, TextField
from datetime import datetime
db = SqliteDatabase('message_board.db')
class MessageBoard(Model):
nickname = CharField()
pub_datetime = DateTimeField()
message_content = TextField()
class Meta:
database = db # 指定資料庫
table_name = 'message_board' # 自定義資料表名,不設定則自動根據類名推導
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())
接著我們只需要在對應Dash
應用的app.py
中呼叫model.py
中的相關功能即可,效果如下(動圖錄制有些花屏,大家可以自己執行嘗試,效果更佳):
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='輸入留言內容:', 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)
有關peewee
的內容非常豐富,想要完全記住不太現實,大家可以養成多查官網http://docs.peewee-orm.com/en/latest/
的習慣,內容非常詳細生動,給官方點個贊!
以上就是本文的全部內容,歡迎在評論區發表你的意見和想法。