zoukankan      html  css  js  c++  java
  • (数据科学学习手札120)Python+Dash快速web应用开发——整合数据库

    本文示例代码已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes

    1 简介

       这是我的系列教程Python+Dash快速web应用开发的第十七期,在之前的各期教程中,我们针对Dash中各种基础且常用的概念展开了学习,但一直没有针对与数据库之间交互进行专门的介绍,只是在某些示例中利用pandasSQLAlchemy等工具简陋地操作数据库。

      而在今天的教程中,我就将带大家学习在Dash中利用简单好用的ORMpeewee,快速高效地将数据库整合进Dash应用中。

    图1

    2 利用peewee在Dash中整合数据库

      说起peewee,很多使用过ORM(Object Relational Mapping,对象关系映射)工具的朋友都听说过,它跟SQLAlchemy等框架从功能上看都大同小异,目的都是为了不写SQL,而是利用面向对象编程的方式,在Python中实现常用的SQL功能。

    图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数据库中创建对应的表:

    图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))
    

      执行完上述命令后旋即会更新到数据库表中:

    图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()
    )
    
    图5

    2.3 从表中删除数据

      对于已存在数据的表,进行数据删除可以使用到delete()方法其后再链式上where()来声明判断条件,最后同样跟上execute()方法执行即可,如果要清空整张表则不用加where(),譬如我们要删除level小于3的记录:

    # 删除level小于3的记录
    Model1.delete().where(Model1.level < 3).execute()
    
    图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()
    
    图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)
    
    图8
    # 获取查询结果方式2:
    query_results = Model1.select().where(Model1.level > 2).dicts()
    list(query_results)
    
    图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中的相关功能即可,效果如下(动图录制有些花屏,大家可以自己运行尝试,效果更佳):

    图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/的习惯,内容非常详细生动,给官方点个赞!

      以上就是本文的全部内容,欢迎在评论区发表你的意见和想法。

  • 相关阅读:
    0902-用GAN生成动漫头像
    0901-生成对抗网络GAN的原理简介
    AES加密
    排序问题
    js中0.1+0.2!=0.3的问题
    关于JavaScript中Number整数最大长度的一个疑问
    IEEE 754标准
    关于浏览器接口Preview中的数值和postman中获取到的不一致问题
    .Net Core 配置之long类型 前端精度丢失和时间格式设置
    .netcore GRPC根据协议生成代码,以及去掉非空判断
  • 原文地址:https://www.cnblogs.com/feffery/p/14748675.html
Copyright © 2011-2022 走看看