美文网首页Head First Python
《Head First Python》Ch7:使用数据库

《Head First Python》Ch7:使用数据库

作者: 老A不加V | 来源:发表于2021-02-18 22:43 被阅读0次

    一、安装MySQL

    MySQL Community Downloads
    windows10上安装mysql(详细步骤)

    二、为Python安装一个MySQL数据库驱动程序

    MySQL-Connector/Python无法用pip安装,手动安装MySQL-Connector/Python驱动程序:

    MySQL-Connector/Python

    选择源码(Source Code)

    在页面最下方找到平台独立(Platform Independent)的压缩包

    (Windows下载ZIP文件,Linux和Mac OS可下载GZ)

    下载解压为文件夹mysql-connector-python-8.0.12,作为管理员打开终端窗口,需要在mysql-connector-python-8.0.12中执行以下命令:

    py -3 setup.py install

    Linux或Mac OS,则使用以下命令:

    sudo -H python3 setup.py install

    建议环境PATH配置修改!

    三.创建Web应用的数据库和表

    使用数据库 具体使用Python的DB-API (如何解决error2003,error1064,error1045)

    启动MySQL控制台,

    要作为MySQL数据库管理员登录(使用根目录ID

    注意:

    在安装mysql5.7版本时,经常会遇到mysql -u root -p直接回车登陆不上的情况,原因在于5.7版本在安装时自动给了一个随机密码,坑爹的是在init步骤的时候不像linux系统会给出命令行提示,需要手动在mysql目录下搜索*.err,以文本形式打开才能看到如下内容:

    016-02-25T15:09:43.033062Z 1 [Note] A temporary password is generated for root@localhost:>mso<k70mrWe

    红色字母即为第一次的登陆密码,记得加双引号。

    下面是启动控制台的命令:

    mysql -u root -p

    创建一个数据库,名为vsearchlogDB

    以下为创建数据库的命令:

    mysql> create database vsearchlogDB;

    参考head first python创建新的用户和口令失败,如下

    mysql> grant all on vsearchlogDB.* to 'vsearch' identified by 'vsearchpasswd';

    于是出现错误error1064:

    解决方法:create user语句创建一个用户,然后再grant权限,成功。

    mysql> create user vsearch identified by 'vsearchpasswd';

    mysql> grant all on vsearchlogDB.* to 'vsearch';

    推测是mysql8以后不允许直接建立用户并给权限的操作了。

    而后用新创建的vsearch用户登录:

    mysql -u vsearch -p vsearchlogDB

    却又出现错误error1045:

    解决方案:mysql -u root -p 直接回车两次

    use +数据库名,即可

    然后就可以存放数据:

    可以看到log表已经存在,而且结构符合这个Web应用的日志记录需要,输入quit退出控制台。

    Python中的BD-API

           在没有DB-API之前,接口程序混乱。具体的就是说,由于最底层用的数据库技术不同,所以在应用程序层就要针对特定的数据库进行特定的编码,如果要改变一个版本所使用的底层数据库,那么之前编写的应用程序中关于数据库的代码也要进行相应的改变。

    python DB-API:python访问数据库的统一接口规范。

           这里有一个约定:程序员使用python与任何底层数据库交互时都使用DB-API,而不论这个数据库技术具体是什么,这么做是因为,利用驱动程序,程序员无需了解与数据库具体API交互的底层细节,因为DB-API在代码与驱动程序之间提供了一个抽象层,这里的想法是:通过使用DB-API编程,可以根据需要替换掉底层数据库技术,而无须丢弃现有的代码。

    1、定义连接属性

    连接到MySQL服务器时需要4部分信息:

    (1)运行MySQL服务器的计算机(称为主机)的IP地址/主机名;

    (2)要使用的用户ID;

    (3)与用户ID关联的口令;

    (4)这一用户ID想要交互的数据库名。

    >>> dbconfig = {'host':  '127.0.0.1',               

      #服务器在我们本地计算机上运行。所以“host”使用localhost IP地址。

    'user':  'vsearch',

    #这一章前面创建的“vsearch”用户ID赋给“user”键。

    'password':  'vseacrh',

    “password”键赋为我们的用户ID相应的正确口令。

    'database':  'vsearchlogDB', }

    数据库名(这里就是“vsearchlogDB”)赋至“database”键。

    2、导入数据库驱动程序

    >>> import mysql.connector

    3、建立与MySQL服务器的一个连接

    conn = mysql.connector.connect(**dbconfig)

    (**)记法告诉connect函数用一个变量提供了参数字典,如果看到这种记法,connect函数会把这个字典展开为4个单独的参数,然后在connect函数中使用这些参数来建立连接。

    4、打开一个游标

    要向数据库发送SQL命令(通过刚才打开的连接)以及从数据库接收结果,我们需要一个游标,可以把游标理解成数据库中文件的句柄。

    创建游标很简单:只需要调用cursor方法,每个连接对象都有这个方法。

    接下来需要一个翻译机,也就是游标(cursor),代码如下:

    >>> cursor=conn.cursor()

    然后就可以用这个游标,使用SQL语言与服务器端的数据库通信了。

    前面我们所做的这些操作,在无论使用什么数据库时,都是通用的。只是连接属性可能不同而已。

    5、完成SQL查询

    可以利用cursor变量向MySQL发送SQL查询,以及获取MySQL处理查询生成的结果,我认为的是这个跟ajax中的request请求对象很像。

    _SQL = """show tables"""

    cursor.execute(_SQL);

    一般用三重双引号来编写要发送给数据库服务器的SQL,之所以使用三重双引号,是因为三重双引号可以暂定不启用python解释器中的“行末即语句结束的规则”。

    执行cursor.execute()命令时,这个SQL查询会被发送到MySQL的服务器,它会执行这个查询(假设正确),不过结果并不会立即显示,必须要请求结果。

    注意,SQL代码是存在一个字符串中的,这样python就不会试图去理解这句代码的意思,而是老老实实通过cursor把这句代码传到服务器端,并运行。

    然而它并不会返回一个结果,需要我们自己去请求结果。

    请求结果有三种方式:

    fetchone:请求一行结果;

    fetchmany:请求指定行结果;

    feychall:请求所有结果。

    我们选择请求所有结果。代码如下:

    >>> res=cursor.fetchall()

    >>> res

    [('log', ) ]

    这里要特别注意点是:fetchall()方法,它返回的是客户端缓冲区现在还没有被影响的记录。注意返回的是一个列表,列表中的元素是元组。也就是说。fetchall返回值的类型一定是一个元组列表,即使只有一个元素也不例外。由于我们在3.3中只建了一张名为log的表,因此这里只有log一个结果。

    如果想要查看表中的内容怎么办呢?用SQL的describe语句。如下:

    >>> _SQL="""describe log"""

    >>> cursor.execute(_SQL)

    >>> res=cursor.fetchall()

    >>> res

    [('id', b'int', 'NO', 'PRI', None, 'auto_increment'), ('ts', b'timestamp', 'YES', '', b'CURRENT_TIMESTAMP', 'DEFAULT_GENERATED'), ('phrase', b'varchar(128)', 'NO', '', None, ''), ('letters', b'varchar(32)', 'NO', '', None, ''), ('ip', b'varchar(16)', 'NO', '', None, ''), ('browser_string', b'varchar(256)', 'NO', '', None, ''), ('results', b'varchar(64)', 'NO', '', None, '')]

    感觉有点乱。但还是可以看出,返回的仍是一个列表(最外面是方括号[]),列表的每个元素都是元组(次外层是括号()),元组元素为字符串,若元组中元素为空,则元组元素不显示。

    表中每一行对应一个元组。这样来看,按行输出比较合适,效果如下:

    >>> for row in res:

            print(row)

    还不错。res中就保存了log表中的所有数据。

    下面来看如何在表中添加数据。使用SQL的insert语句。如下:

    >>> _SQL="""insert into log

            (phrase,letters,ip,browser_string,results)

            values

            ('hitch-hicker','aeiou','127.0.0.1','Firefox',"{'e','i'}")"""

    >>> cursor.execute(_SQL)

           然而这种插入操作很蠢,为什么呢?观察_SQL这个变量,它是一句SQL代码,对于insert操作,每次都要重新生成一条代码来插入不同的数据,这太低效了,如果有一种方法,可以将insert语句与待插入内容分开,要用的时候再合并,也就是说,把insert语句看成一个函数,可重用,这就好了。有这种方法。代码如下:

    >>> _SQL="""insert into log

            (phrase,letters,ip,browser_string,results)

            values

            (%s,%s,%s,%s,%s)"""

    >>> cursor.execute(_SQL,('hitch-hiker','xyz','127.0.0.1','Safari','set()'))

           _SQL并没有具体说明要插入的内容,而是用五个占位符%s来进行指定。在实际应用时,只需要在execute后加入一个元组,元组内容对应占位符即可。

           然后查看表中内容是否添加成功。使用select命令。但很有可能select无法返回任何数据。因为,当使用insert操作时,由于写数据库是一个开销很大的操作,所以数据库系统会缓存insert,然后再一次性应用全部的insert。这带来的问题就是,刚刚插入的数据可能访问不到。但是可以通过事务提交(conn.commit())来强制数据库系统将所有可能缓存的数据全部提交到数据库表中,这样既可解决访问不到的问题。

    注意describe是用于描述一个表的,它描述了表的属性,而不管表的内容;select则用于显示表的内容,而不显示表的属性。代码如下:

    >>> conn.commit()

    >>> _SQL="""select * from log"""

    >>> cursor.execute(_SQL)

    >>> for row in cursor.fetchall():

            print(row)

    得到结果:

    6、关闭游标和连接

    数据提交到数据库表中后,要进行清理,关闭游标和连接。

    >>> cursor.close()

    True

    >>> conn.close()

    这是因为数据库系统是一组有限的资源,不关闭会导致资源严重浪费,其它用户无法访问。

    四、创建代码处理Web应用的数据库和表

    下面是回顾最近做的一个项目中关于数据库连接的源码:

    #定义你的连接属性

    dbconfig = {"host": "127.0.0.1",

    "user": "vsearch",

    "password": "vsearch",

    "database": "vsearchlogDB",

    }

    #导入数据库驱动程序

    import mysql.connector

    #建立一个连接并创建一个游标

    conn = mysql.connector.connect(**dbconfig)

    cursor = conn.cursor()

    #将查询赋至一个字符串(注意5个占位符参数)

    _SQL = """insert into log

    (phrase, letters, ip, browser_string, results)

    values

    (%s, %s, %s, %s, %s)"""

    #将查询发送到服务器,记得要为各个参数提供值(以元组的形式)

    cursor.execute(_SQL, ("afeng", "jingjing", "127.0.0.1", "opera", "{'x', 'y'}"))

    #强制要求数据库写数据

    conn.commit()

    #从表获取(刚写入的)数据,逐行显示输出。

    _SQL = """select * from log"""

    cursor.execute(_SQL)

    for row in cursor.fetchall():

         print(row)

    #完成时进行清理

    cursor.close()

    conn.close()

    由于有了以上的铺垫,我们现在要实现以下功能:修改Webapp的log_request函数,让它保存的数据不是存在一个txt里,而是存在SQL数据库中。

    原代码如下:

    deflog_request(req:'flask_request',res:str)->None:

        withopen('vsearch.log','a')aslog:

            print(req.form,req.remote_addr,req.user_agent,res,file=log,sep='|')

    修改之后,要实现与数据库的连接,实现写入等功能,其实蛮简单,如下:

    deflog_request(req:'flask_request',res:str)->None:

           dbconfig={'host':'127.0.0.1',

                           'user':'vsearch',

                           'password':'vsearchpasswd',

                          'database':'vsearchlogDB',}

        import mysql.connector

         conn=mysql.connector.connect(**dbconfig)

         cursor=conn.cursor()

        _SQL="""insert into log

                   (phrase,letters,ip,browser_string,results)

                    values

                   (%s,%s,%s,%s,%s)"""

      cursor.execute(_SQL, (req.form['phrase'],

                                          req.form['letters'],

                                          req.remote_addr,

                                          req.user_agent.browser,

                                          res,))

         conn.commit()

         cursor.close()

         conn.close()

           有两点要注意,其一,是import的位置,不要在函数内部;其二,对于数据的写入,req.form是一个字典,有两项,一项是phrase,即输入;一项是letters,即输出。      

         另外,注意第四项写入的元素req.user_agent.browser,不再是整个浏览器串(存储在req.user_agent),我们只抽取出浏览器名。

    应用新修改的vsearch4web.py:

    C:\webapps> py -3 vsearch4web.py

    现在我们发现了一个新的问题:

    对于那些可能会用到多次的功能,如建立连接、断开连接等,是否可以实现重用呢?

    记住“建立、处理、清理”模式!

    如果每次修改SQL都需要这样繁琐的一步步建立和断开连接,一点都不优美。联想到之前的打开-关闭文件,我们也可以用with来实现这一点。如下所示:

    with UseDatabase(dbconfig) as cursor:

    这是使用with版本的代码。但是还不能工作,因为我们还没有写Usedatabase这一函数的上下文管理器,这里看看就好。

    使用上下文管理器需要创建类,在下面几章,我们将学习如何创建类。

    相关文章

      网友评论

        本文标题:《Head First Python》Ch7:使用数据库

        本文链接:https://www.haomeiwen.com/subject/cipnxltx.html