(資料科學學習手札120)Python+Dash快速web應用開發——整合資料庫

費弗裡發表於2021-05-09

本文示例程式碼已上傳至我的Github倉庫https://github.com/CNFeffery/DataScienceStudyNotes

1 簡介

   這是我的系列教程Python+Dash快速web應用開發的第十七期,在之前的各期教程中,我們針對Dash中各種基礎且常用的概念展開了學習,但一直沒有針對與資料庫之間互動進行專門的介紹,只是在某些示例中利用pandasSQLAlchemy等工具簡陋地運算元據庫。

  而在今天的教程中,我就將帶大家學習在Dash中利用簡單好用的ORMpeewee,快速高效地將資料庫整合進Dash應用中。

(資料科學學習手札120)Python+Dash快速web應用開發——整合資料庫
圖1

2 利用peewee在Dash中整合資料庫

  說起peewee,很多使用過ORM(Object Relational Mapping,物件關係對映)工具的朋友都聽說過,它跟SQLAlchemy等框架從功能上看都大同小異,目的都是為了不寫SQL,而是利用物件導向程式設計的方式,在Python中實現常用的SQL功能。

(資料科學學習手札120)Python+Dash快速web應用開發——整合資料庫
圖2

  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資料庫中建立對應的表:

(資料科學學習手札120)Python+Dash快速web應用開發——整合資料庫
圖3

  而除了最簡單的SQLite之外,peewee還支援MySQLPostgreSQL,你可以在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))

  執行完上述命令後旋即會更新到資料庫表中:

(資料科學學習手札120)Python+Dash快速web應用開發——整合資料庫
圖4
  • 插入多條資料

  在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()
)
(資料科學學習手札120)Python+Dash快速web應用開發——整合資料庫
圖5

2.3 從表中刪除資料

  對於已存在資料的表,進行資料刪除可以使用到delete()方法其後再鏈式上where()來宣告判斷條件,最後同樣跟上execute()方法執行即可,如果要清空整張表則不用加where(),譬如我們要刪除level小於3的記錄:

# 刪除level小於3的記錄
Model1.delete().where(Model1.level < 3).execute()
(資料科學學習手札120)Python+Dash快速web應用開發——整合資料庫
圖6

  更多關於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()
(資料科學學習手札120)Python+Dash快速web應用開發——整合資料庫
圖7

  更多內容可參考官方文件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)
(資料科學學習手札120)Python+Dash快速web應用開發——整合資料庫
圖8
# 獲取查詢結果方式2:
query_results = Model1.select().where(Model1.level > 2).dicts()
list(query_results)
(資料科學學習手札120)Python+Dash快速web應用開發——整合資料庫
圖9

  而有關跨表連線等進階的查詢操作,請參考官方文件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實現線上留言板功能

  getpeewee的常用基礎用法之後,我們回到本文的重點——結合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中的相關功能即可,效果如下(動圖錄制有些花屏,大家可以自己執行嘗試,效果更佳):

(資料科學學習手札120)Python+Dash快速web應用開發——整合資料庫
圖10

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/的習慣,內容非常詳細生動,給官方點個贊!

  以上就是本文的全部內容,歡迎在評論區發表你的意見和想法。

相關文章