一、安装MySQL
MySQL Community Downloads
windows10上安装mysql(详细步骤)
二、为Python安装一个MySQL数据库驱动程序
MySQL-Connector/Python无法用pip安装,手动安装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这一函数的上下文管理器,这里看看就好。
使用上下文管理器需要创建类,在下面几章,我们将学习如何创建类。
网友评论