跳至主要內容

18-Python SQLAIchemy 操作 MySQL(关系型数据库)「2023年」

AI悦创原创Python 网络爬虫专栏Crawler大约 60 分钟...约 17959 字

原名

Python 之站在高层框架下的 SQLAIchemy 操作 MySQL(关系型数据库)

一、Python 安装那些事

1.1 Python 安装

下载方法

进入官网:https://www.python.orgopen in new window

在这里插入图片描述
在这里插入图片描述

如图:

  1. 选择上方 Downloads 选项
  2. 在弹出的选项框中选择自己对应的系统(注:若直接点击右边的灰色按钮,将下载的是 32 位)

进入下载页面,如图:

  1. 为 64 位文件下载
  2. 为 32 位文件下载

选择您对应的文件下载。

安装注意事项:

(图片来源于网络)

自定义选项,可以选择文件存放位置等,使得 Python 更符合我们的操作习惯。

默认安装:一路 Next 到底,安装更方便、更快速。

特别注意:图中箭头指向处一定要记得勾选上。否则得手动配置环境变量了哦。

Q:如何配置环境变量呢?

A:控制面板—系统与安全—系统—高级系统设置—环境变量—系统变量—双击 path—进入编辑环境变量窗口后在空白处填入 Python 所在路径—一路确定。

检查

安装完 Python 后,Win+R 打开运行窗口输入 cmd,进入命令行模式,输入 python。若如下图显示 Python 版本号及其他指令则表示 Python 安装成功。

1.2 Python 编译器 Sublime

官网:http://www.sublimetext.com/open in new window

选择该编辑器的原因:

  1. 不需要过多的编程基础,快速上手
  2. 启动运行速度快
  3. 最关键的原因——免费

常见问题

使用快捷键 Ctrl+B 无法运行结果,可以尝试 Ctrl+Shift+P,在弹出的窗口中选择 Bulid With: Python

或选择上方的 Tool 选项中的 Build With 选项,在弹出的窗口中选择 Python。

二、Python 语言快速入门

本节虽是零基础友好文,但也有对一些知识点的深度拓展,有编程基础的看官也可以选择性观看哦!

2.1 Python 交互式模式与命令行模式

2.1.1 命令行模式

1. 进入方式

Windows:

  1. 点击开始,运行,CMD 回车
  2. 按 WIN+R,CMD 回车

Mac:

  1. 打开应用菜单中的 Launchpad,找到并打开【其他】文件夹,点击【终端】
  2. 打开 Finder 窗口,在「应用程序」目录中直接搜索“终端”关键字

2. 提示符

在不同的操作系统环境下,命令提示符各不相同,以 Windows 为例:它的提示符为:

C:\机器名\用户名>
在这里插入图片描述
在这里插入图片描述

2.1.2 交互式模式

1. 进入方式

在命令模式下输入 Python 指令即可进入,输入 exit(),便会退出交互式模式。

2. 提示符

>>>
在这里插入图片描述
在这里插入图片描述

区别

  1. py 文件只能在命令行中运行;
  2. Python 交互模式的代码是输入一行、执行一行;而命令行模式下直接运行 .py 文件是一次性执行该文件内的所有代码。

由此看来,Python 交互模式主要是用来调试代码的。

2.2 数据类型和变量

Python 中主要的数据类型有:整数(int)、浮点数(float)、布尔值(bool)、字符串(str)、列表(list)、元组(tuple)、字典(dict)、集合(set)。

  2                 #整数  (int)
  3.1314526         #浮点数 (float)
  True              #布尔值 (bool)
  "1"               #字符串 (str)
  [1,2,"a"]         #列表(list)
  (1,2,"a")         #元组(tuple)
  {"name":"小明"}   #字典(dict)

在 Python 中,你可以使用 # 来注释相关信息,注释的信息 IDE 在编译的时候,会自动忽略。

2.2.1 整数

与数学中整数概念一致,共有 4 种进制表示:二进制、八进制、十进制和十六进制。默认情况,整数采用十进制。

在这里插入图片描述
在这里插入图片描述

(图片来源于网络)

2.2.2 浮点数

表示有小数点的数值。浮点数有两种表示方法:小数表示和科学计数法表示。(注:计算器或电脑表达 10 的幂是一般是用 E 或 e,即 2.88714E13=28871400000000)

2.2.3 布尔值

布尔值在 Python 中有两个量:True 和 False,对应的值分别是 1 和 0(True、False 注意大小写)。

代码如下:

 var1 = 12
 var2 = 12
 var3 = 13
 print(var1==var2) #输出True
 print(var1==var3) #输出False

var1==var2 中的 == 是比较符,比较 var1 是否等于 var2,若等于则为真(True),否则为假(False)。

另外,布尔值可以用 and(与)、or(或)和 not(非)进行运算。

代码如下:

与运算:铁面无私,要求所有都True,否则输出结果就为FalseTrue and True #True
True and False #False
False and False #False

代码如下:

或运算:要求不高,只要有一个为True输出的结果就为TrueTrue or True #True
True or False #True
False or False #False

代码如下:

非运算:老是唱反调,输入True,它给你输出False,反之亦然。(特别注意:它是一个单目运算符)
not True #False 
not False #True

2.2.4 字符串

字符串是以单引号 ' 或双引号 " 括起来的任意文本,如 'aaa'"abc"。'' 或 "" 本身只是一种表示方式,不是字符串的一部分,因此,字符串 'aaa' 只有 aaa 这 3 个字符。

若字符串里已经包含了 '" 了呢?我们可以用转义字符 \ 来标识,比如:

you’re 的字符串表示为:

"you\' re"

若字符串内容包含 ' 的同时也包含了 \ 呢? 那我们可以用 \\ 来表示,代码如下:

"you\\'re"

2.2.5 列表

在 Python 中,列表是比较重要的一个数据容器。

代码如下:

 list1 = [1,2,3,4,5]
 list2 = ["AI悦创","GitChat","Fly"]

列表是具有索引的,因此想要访问一个列表中的数值,只需要列表名 + 索引值就能够得到了。

print(list1[2])  # 输出:3
print(list2[0])  #输出:AI悦创
# 示例二
lists = ['a','b','c']
lists.append('d')
print(lists)
print(len(lists))
lists.insert(0,'mm')
lists.pop()#删除最后一个元素
print(lists)
# 输出
['a', 'b', 'c', 'd']
4
['mm', 'a', 'b', 'c']

2.2.6 元组

元组创建很简单,只需要在括号中添加元素,并使用逗号隔开即可。

代码实例:

tup1=('aaa',1,'bbb',2)

需注意:组中只包含一个元素时,需要在元素后面添加逗号,否则括号会被当作运算符使用。

>>> tup1=(1)  
>>> type(tup1)
<class 'int'> 
>>> tup2=(1,) 
>>> type(tup2)
<class 'tuple'>

列表与元组的区别

不知大家在学完列表与元组后,有没有发现两者有些类似, 主要的不同在于:

  1. 元组使用小括号,列表使用方括号。
  2. 列表是动态的,长度大小不固定,可以随意地增加、删减或者改变元素(可变)。
  3. 元组是静态的,长度大小固定,无法增加删减或者改变(不可变)。

**偷偷告诉你哦:**其实是列表与元组最重要的区别,而这样的差异,势必会影响两者存储方式。我们可以来看下面的例子:

l = [1, 2, 3]
l.__sizeof__()
64
tup = (1, 2, 3)
tup.__sizeof__()
48

你可以看到,对列表和元组,我们放置了相同的元素,但是元组的存储空间,却比列表要少 16 字节。这是为什么呢?

事实上,由于列表是动态的,所以它需要存储指针,来指向对应的元素(上述例子中,对于 int 型,8 字节)。另外,由于列表可变,所以需要额外存储已经分配的长度大小(8 字节),这样才可以实时追踪列表空间的使用情况,当空间不足时,及时分配额外空间。

代码实例:

l = []
l.__sizeof__() // 空列表的存储空间为 40 字节
40
l.append(1)
l.__sizeof__() 
72 // 加入了元素 1 之后,列表为其分配了可以存储 4 个元素的空间 (72 - 40)/8 = 4
l.append(2) 
l.__sizeof__()
72 // 由于之前分配了空间,所以加入元素 2,列表空间不变
l.append(3)
l.__sizeof__() 
72 // 同上
l.append(4)
l.__sizeof__() 
72 // 同上
l.append(5)
l.__sizeof__() 
104 // 加入元素 5 之后,列表的空间不足,所以又额外分配了可以存储 4 个元素的空间

上面的例子,大概描述了列表空间分配的过程。我们可以看到,为了减小每次增加/删减操作时空间分配的开销,Python 每次分配空间时都会额外多分配一些,这样的机制(over-allocating)保证了其操作的高效性:增加/删除的时间复杂度均为 O(1)。

但是对于元组,情况就不同了。元组长度大小固定,元素不可变,所以存储空间固定。

看了前面的分析,你也许会觉得,这样的差异可以忽略不计。但是想象一下,如果列表和元组存储元素的个数是一亿,十亿甚至更大数量级时,你还能忽略这样的差异吗?

所以我们可以得出结论:元组要比列表更加轻量级一些,所以总体上来说,元组的性能速度要略优于列表。

2.2.7 字典

字典是一种特殊的列表,字典中的每一对元素分为键(key)和值(value)。对值的增删改查,都是通过键来完成的。注意:字典中的建 /KEY 需是不可变数据类型,如:整型 int、浮点型 float、字符串型 string 和元组 tuple。

代码如下:

 brands = {"Tencent":"腾讯","Baidu":"百度","Alibaba":"阿里巴巴"}

 brands["Tencent"]  #获取键值为"Tencent"的value
 del brands["Tencent"] #删除腾讯
 brands.values[] #得到所有的value值
 brands.get("Tencent")  # 获取键值为"Tencent"的value

2.2.8 集合

集合是一个无序的不重复元素序列,我们可以通过 {} 或者 set() 来创建它。

代码如下:

set1={'a','aa','aaa','aaaa'} #{'aaa', 'aa', 'aaaa', 'a'}
set1=set(['a','aa','aaa','aaaa'])
print(set1)  #{'aaaa', 'aa', 'a', 'aaa'}

注意:创建一个空集合必须用 set() 而不是 {},因为 { } 是用来创建一个空字典。

>>> s={}
>>> type(s)
<class 'dict'>

拓展

在数据类型中,我们多次提到可变对象,不可变对象,那什么是可变对象,什么是不可变对象呢?

先放个小提示:

  • Python 不可变对象:int、float、tuple、string
  • Python 可变对象:list、dict、set

从字面意思上理解,“可变对象”是指可以使其元素发生改变的对象,不可变对象就是不可以发生改变,由此我们可以猜测,两者的区别在于:能否对其元素进行修改。

而我们通常是通过什么方式来尝试修改对象呢?俗话说,“东西不在多,而在常用”,这里,我们一起介绍“增删改查”这几种常用的方法。

以可变对象列表为例,添加:append、insert。

代码如下:

>>> list=["a","b"]
>>> list.append("c") # append(元素),将元素添加到列表里
>>> print(list)
['a', 'b', 'c']

>>> list.insert(0,"d")#insert(索引,元素),将元素添加到指定位置
>>> print(list)
['d', 'a', 'b', 'c']

删除:remove()、pop(索引)、pop()

运行如下代码:

>>> list.remove("d")#remove(元素),删去list中看不顺眼的元素
>>> list
['a', 'b', 'c']
>>> list.pop(1)
'b'#被删掉的元素
>>> print(list)
['a', 'c']#pop(索引),删去制定位置的元素
>>> list.pop()
'c'#被删掉的元素
>>> print(list)#pop(),默认删去最后一个元素
['a']

修改:list [索引] = 元素

代码如下:

>>> list=['a','c']
>>> list[0]='b'#替换制定位置的元素
>>> print(list)
['b','c']

查找:list [索引]

代码如下:

>>> list=['b','c']
>>> list[1]#查找指定位置的元素
'c'

可变对象 list 全部修改成功。现在,我们再来尝试对不可变对象 tuple 进行简单的修改,看看会发生什么?

>>> tuple1=(1,2,3,4)
>>> tuple1[0]=5
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment#报错

通过以上对 list、tuple 的修改,我们可以证实不可变对象与可变对象的最简单明了的区别:可变对象的元素是可以修改的,不可变对象的元素是不能进行修改的。

2.2.9 变量

当我们在 CMD 控制台输入 1+1 的时候,控制台会输出 2。但是,如果我们要在之后的计算中继续使用这个 2 该怎么办呢?我们就需要通过一个“变量”来存储我们需要的值。

代码如下:

a=1+1  #这里a就是一个变量,用来存储 1+1产生的2

如上面的“栗子”所示:Python 中的变量赋值不需要类型声明。

偷偷告诉你哦:创建变量时会在内存中开辟一个空间。基于变量的数据类型(若没交代数据类型,则默认为整数),解释器会分配指定内存,并决定什么数据可以被存储在内存中。

拓展

是不是觉得很奇妙?b=a 按理说 a 变了,b 也应该跟着变呀!

让我们一起做个假设:

假设开发商 = 内存,变量 = 房子,变量存储的值 = 住户,在 b=a 前,a=1 的大趋势使得开发商把 a 房子建造好了,当 b=a 复制时,开发商又马不停蹄的画了块内存建了 b 房子,且 b 房子和 a 房子里都住着数值 1,因此当 a=4,使得 a 房子换了新住户,但这不能影响到 b 房子住户——数值 1 的居住。

2.3 条件、循环和其他语句

Python 使用 if 和 else 来作为条件判断语句。

代码实例:

# 判断语句:if … else …
i = 1
if i == 1:
    print("Yes,it is 1")
else:
    print("No,it is not 1")
# if … else … 是经典的判断语句,需要注意的是在 if expression 后面有个冒号,同样在 else 后面也存在冒号。

上面的语句用来判断变量 i 是否等于 1。请注意:Python 对缩进是极重视的。所以在写判断语句的时候,需要注意缩进是否在同一个区域。

Python 支持 for 循环和 while 循环。循环语句和 if、else 语句类似,比如都需要加冒号,语句体需要缩进。

for i in range(1,10):
  print(i)

上面的语句是输出 1 到 10 之间的数,请注意,range(1,10) 的范围是从 1 到 9,不包含 10。

i = 1
while (i<10):
    i += 1
    if i!= 8:
          continue
    else:
          break

上面的语句中,break 关键词的意思是:跳出循环;continue 的意思是:继续循环。

2.3.1 函数

我们平常使用的 print() 和 type(),这两个都是函数。对于重复性的代码段,我们不需要每次都写出,只需要通过函数的名称调用就可以了。

定义函数的关键字是 def,定义的方式和 for 循环差不多。

代码如下:

def function(param):  # function为函数名,param为参数
    i = 1
    return i  # f返回值
为了讲解得更形象,我们来写一个 a+b 求和的函数。

def getsum(a,b):  #定义函数名为getSum,参数为a,b
    sum = a+b;
    return sum;  #返回a+b的和,sum

print(getsum(1, 2))

定义完成后,我们就可以在程序的其他地方,通过调用 getSum(a,b) 来使用这个函数。

2.3.2 文件

Python 提供了丰富且易用的文件操作函数,我们将常见的操作快速学习一下。

open()

代码如下:

open("abc.txt","r")  
#open()为Python 内置的文件函数,用来打开文件,“abc.txt”为目标文件名,"r"代表以只读方式打开文件,其他的还有“w"和"a"模式

read()

打开的文件,必须通过 .read() 方法才能得到数据。

file = open("abc.txt","r")
words = file.read()

关于详细的读取,可以有兴趣的可以参考这篇文章:Python 中的文件基本操作合集 with open~open in new window

三、关系型数据库入门

3.1 进入主题

3.1.1 MySQL 与 Flask

MySQL

MySQL 是最流行的关系型数据库管理系统之一,在 Web 应用,MySQL 是最好的 RDBMS (关系型数据库管理系统)应用软件之一。

Flask

Python Web 开发界主力——Flask。使用 SQLAlchemy 进行数据库开发。使用 ORM 是大势所趋。

3.1.2 MySQL

数据库(Database)是按照数据结构来组织、存储和管理数据的仓库。

每个数据库都有一个或多个不同的 API 用于创建,访问,管理,搜索和复制所保存的数据库。CURD。

数据库的三大范式

范式内容
第一范式第一范式是最基本的范式。如果数据库表中的所有字段值都是不可分解的原子值
就说明该数据库表满足了第一范式。
第二范式第二范式需要确保数据库表中的每一列都和主键相关而不能只与主键的某一部分相关
(主要针对联合主键而言)。也就是说在一个数据库表中,一个表中只能保存一种数据,
不可以把多种数据保存在同一张数据库表中。
第三范式第三范式需要确保数据表中的每一列数据都和主键直接相关,而不能间接相关。

3.2 数据库

关系型数据库,是建立在关系关系模型的基础上的数据库,借助于集合、代数、等数学概念和方法来处理数据库中的数据。

特点:

  1. 数据以表格的形式出现
  2. 每行/为各种记录名称
  3. 每列/为记录名称所对应的数据域
  4. 许多的行和列组成一张表单
  5. 若干的表单组成 database

3.2.1 安装 MySQL

https://www.runoob.com/mysql/mysql-install.htmlopen in new window

检测是否安装成功,运行程序命令:

mysql -u root -p
# Enter 后输入密码

接下来我们创建一个数据库,供我们接下来学习使用:

登陆数据库
~ mysql -uroot -p123123 # 123123 是数据库密码,记得换成你自己的
mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 8
Server version: 8.0.31 Homebrew

Copyright (c) 2000, 2022, Oracle and/or its affiliates.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> show databases;  # 查看已有的数据库
+--------------------+
| Database           |
+--------------------+
| aiyc               |
| cava               |
| EasyDataBases      |
| information_schema |
| mysql              |
| performance_schema |
| sys                |
+--------------------+
7 rows in set (0.00 sec)
 













 












3.3 SQLAIchemy 操作 MySQL

3.3.1 SQL alchemy 简介

SQLAlchemy 是 Python 编程语言下的一款 ORM 框架,该框架建立在数据库 API 之上,使用关系对象映射进行数据库操作,简言之便是:将对象转换成 SQL,然后使用数据 API 执行 SQL 并获取执行结果。

3.3.2 ORM 方法论基于三个核心原则

名称作用
简单以最基本的形式建模数据
传达性数据库结构被任何人都能理解的语言文档化
精确性基于数据模型创建正确标准化了的结构

安装库:

教程
pip install sqlalchemy pymysql
# sqlalchemy: 是我们的 ORM
# pymysql:是我们能够执行这个 sqlalchemy 时候的相当于一个网关

根据配置文件的不同调用不同的数据库 API,从而实现对数据库的操作,如:

'数据库类型+数据库驱动名称://用户名:口令@机器地址:端口号/数据库名'

mysql+pymysql://<username>:<password>@<host>|<dbname>[?<options>]

mysql+mysqldb://<user>:<password>@<host>[:<port>]/<dbname>
mysql+pymysql://:@/[?]
# mysql+pymysql://<username>:<password>@<host>|<dbname>[?<options>]

mysql+mysqldb://:@[:]/
# mysql+mysqldb://<user>:<password>@<host>[:<port>]/<dbname>

2.7 版本使用 MySQLdb 3.5 版本使用 PyMySQL。

3.4 连接数据库

这里你要连接数据库前,请你创建好数据库(上面已经说明如何创建数据库 test):

教程
from sqlalchemy import create_engine

engine = create_engine(
	"mysql+pymysql://root:123456@127.0.0.1:3306/test",# (里面的 root 要填写你的密码),注意:mysql+pymysql 之间不要加空格
	# "mysql + pymysql://root:root@localhost/test",
	max_overflow = 5, # 超过连接池大小之后,外最多可以创建的链接
	pool_size = 10, # 连接池大小
	echo = True, # 调试信息展示
)

编写完上面的代码,我们需要运行程序,看程序是否报错,没有报错则代码无问题。如果报错,认真对比你和我的代码,按你实际数据库情况来写。

PS:上面的代码时样板代码,咱们连接数据库都是用上面的代码。

3.5 基础知识

SQL AlchemyPython
TextLong str
Booleanbool
BigIntegerint
DateDatetime.data
DateTimeDatetime.datetime
Floatfloat
Stringstr

导入 DateTime 字段,default 传入的是函数,不是执行结果,不需要括号。「回调函数」

假如我们要创建一个表的话,需要导入一个 Table,也就是这样写:

教程
from sqlalchemy import Table
# 当然还需要其他的,例如:Column
from sqlalchemy import Column
# 这时,我们发现,我们导入 Table和Column来自于同一个库,所以我们可以直接写成这一句:
from sqlalchemy import Table,Column
# 接下来我把我们需要用到的库函数导入进来:
from sqlalchemy import Table,Column,String,Integer,Boolean,MetaData
from datetime import datetime

3.6 创建表

代码
from aqlalchemy import Table,Column,String,Integer,Boolean,MetaData
from datetime import datetime

metadata = MetaData() # 取得元数据,介绍数据库
# 获取元数据,这个元数据相当于你对这个数据库的一些描述,你也可以什么都不写。因为,我们没必要在里面写,直接在 sqlalchemy 里面导入 MetaData 就可以了。
test = Table("test", metadata,
             Column("id", Integer(), primary_key=True, autoincrement=True),
             Column("name", String(255)),
             Column("data", DateTime(), default=datetime.now, onupdate=datetime.now),
             Column("main", Boolean(), default=False))

这时候有心急的小伙伴们就直接运行代码,然后会有疑问就直接来问老师我了。老师咋这个也没有创建呀,为什么呢?因为还差一个知识点没给你们讲呀,差一个事物。当然,这个接下来会讲到滴,我先给上面的代码加上一行代码执行以下看看哈:

关键代码
metadata.create_all(engine) # 创建数据表

如果运行正常,会输出一大堆信息:

2020的输出
"C:\Program Files\Python37\python.exe" D:/daima/pycharm_daima/爬虫大师班/07-关系型数据库入门/Create_databases_Table.py
2020-01-11 16:27:40,335 INFO sqlalchemy.engine.base.Engine SHOW VARIABLES LIKE 'sql_mode'
2020-01-11 16:27:40,335 INFO sqlalchemy.engine.base.Engine {}
2020-01-11 16:27:40,343 INFO sqlalchemy.engine.base.Engine SHOW VARIABLES LIKE 'lower_case_table_names'
2020-01-11 16:27:40,344 INFO sqlalchemy.engine.base.Engine {}
C:\Program Files\Python37\lib\site-packages\pymysql\cursors.py:170: Warning: (1366, "Incorrect string value: '\\xBB\\xC6\\xBC\\xD2\\xB1\\xA6...' for column 'VARIABLE_VALUE' at row 75")
  result = self._query(query)
C:\Program Files\Python37\lib\site-packages\pymysql\cursors.py:170: Warning: (1366, "Incorrect string value: '\\xBB\\xC6\\xBC\\xD2\\xB1\\xA6' for column 'VARIABLE_VALUE' at row 94")
  result = self._query(query)
C:\Program Files\Python37\lib\site-packages\pymysql\cursors.py:170: Warning: (1366, "Incorrect string value: '\\xBB\\xC6\\xBC\\xD2\\xB1\\xA6...' for column 'VARIABLE_VALUE' at row 259")
  result = self._query(query)
C:\Program Files\Python37\lib\site-packages\pymysql\cursors.py:170: Warning: (1366, "Incorrect string value: '\\xBB\\xC6\\xBC\\xD2\\xB1\\xA6...' for column 'VARIABLE_VALUE' at row 377")
  result = self._query(query)
C:\Program Files\Python37\lib\site-packages\pymysql\cursors.py:170: Warning: (1366, "Incorrect string value: '\\xBB\\xC6\\xBC\\xD2\\xB1\\xA6...' for column 'VARIABLE_VALUE' at row 403")
  result = self._query(query)
C:\Program Files\Python37\lib\site-packages\pymysql\cursors.py:170: Warning: (1366, "Incorrect string value: '\\xBB\\xC6\\xBC\\xD2\\xB1\\xA6...' for column 'VARIABLE_VALUE' at row 404")
  result = self._query(query)
C:\Program Files\Python37\lib\site-packages\pymysql\cursors.py:170: Warning: (1366, "Incorrect string value: '\\xBB\\xC6\\xBC\\xD2\\xB1\\xA6...' for column 'VARIABLE_VALUE' at row 455")
  result = self._query(query)
C:\Program Files\Python37\lib\site-packages\pymysql\cursors.py:170: Warning: (1366, "Incorrect string value: '\\xD6\\xD0\\xB9\\xFA\\xB1\\xEA...' for column 'VARIABLE_VALUE' at row 484")
  result = self._query(query)
2020-01-11 16:27:40,349 INFO sqlalchemy.engine.base.Engine SELECT DATABASE()
2020-01-11 16:27:40,349 INFO sqlalchemy.engine.base.Engine {}
2020-01-11 16:27:40,350 INFO sqlalchemy.engine.base.Engine show collation where `Charset` = 'utf8mb4' and `Collation` = 'utf8mb4_bin'
2020-01-11 16:27:40,350 INFO sqlalchemy.engine.base.Engine {}
2020-01-11 16:27:40,352 INFO sqlalchemy.engine.base.Engine SELECT CAST('test plain returns' AS CHAR(60)) AS anon_1
2020-01-11 16:27:40,352 INFO sqlalchemy.engine.base.Engine {}
2020-01-11 16:27:40,354 INFO sqlalchemy.engine.base.Engine SELECT CAST('test unicode returns' AS CHAR(60)) AS anon_1
2020-01-11 16:27:40,354 INFO sqlalchemy.engine.base.Engine {}
2020-01-11 16:27:40,355 INFO sqlalchemy.engine.base.Engine SELECT CAST('test collated returns' AS CHAR CHARACTER SET utf8mb4) COLLATE utf8mb4_bin AS anon_1
2020-01-11 16:27:40,355 INFO sqlalchemy.engine.base.Engine {}
2020-01-11 16:27:40,358 INFO sqlalchemy.engine.base.Engine DESCRIBE `test`
2020-01-11 16:27:40,358 INFO sqlalchemy.engine.base.Engine {}
2020-01-11 16:27:40,359 INFO sqlalchemy.engine.base.Engine ROLLBACK
2020-01-11 16:27:40,360 INFO sqlalchemy.engine.base.Engine 
CREATE TABLE test (
	id INTEGER NOT NULL AUTO_INCREMENT, 
	name VARCHAR(255), 
	date DATETIME, 
	man BOOL, 
	PRIMARY KEY (id), 
	CHECK (man IN (0, 1))
)


2020-01-11 16:27:40,360 INFO sqlalchemy.engine.base.Engine {}
2020-01-11 16:27:40,407 INFO sqlalchemy.engine.base.Engine COMMIT

Process finished with exit code 0

扩展:使用 Python 操作之后原本的语句已经被转换为 MySQL 的原生语句:

2020年
# Python 中 sqlalchemy 的语句:
test=Table('test',metadata,
          Column('id',Integer(),primary_key=True,autoincrement=True),
          Column('name',String(255)),
          Column('data',DateTime(),default=datetime.now,onupdate=datetime.now),
          Column('main',Boolean(),default=False),)

# MySQL 原生语句:
CREATE TABLE test (
	id INTEGER NOT NULL AUTO_INCREMENT, 
	name VARCHAR(255), 
	date DATETIME, 
	man BOOL, 
	PRIMARY KEY (id), 
	CHECK (man IN (0, 1))
)

一个学员的提问:

学员:这个是不是得加括号?

我:default 传入是函数,不是执行结果,不需要括号。

我:内部会执行这个回调函数的

学员:括号,加和不加有什么区别呢?

我:default 是一个函数,加了括号就是直接调用了,也就变成了一个值。

学员:不是特别理解

我:default 需要的是一个函数,加了 () 就是执行了这个函数

学员:也就是说如果只需要一个函数就不加 () 如果要加括号就直接运行了?

我:执行函数会返回一个值, 如果加了 () 之后传入 default 就不是一个函数,而是 datetime.now() 的执行结果了。

我:这个函数传进去的目的就是内部会自己调用,程序在用到这个函数的时候会自己调用传入的这个函数,叫做回调函数。(他会自己调用的)

好,扩展之后我们继续:运行完代码之后,(也就是创建完我们的这个表之后)就可以在命令中(也就是已经启动 MySQL)输入:

教程
# 按一下步骤操作,如果你在上面已经操作 MySQL 则可以直接输入以下命令:
desc test;
# 当然,如果你不理解的话,请关掉控制台,让我们从头开始操作,检查刚刚所创建的数据库表1)mysql -u root -p123456 (注意:-p后面所接的是你数据库的密码)2)use test;3)desc test;

操作图示:

虽然,写的是 Python 代码,但是我们的 ORM 会帮我转换的。

名称作用
MetaData()取得元数据,介绍数据库

3.7 MySQL 事务

介绍

MySQL 事务主要用于处理操作量大,复杂度高的数据。比如说,在人员管理系统中,你删除一个人员,你既需要删除人员的基本资料,也要删除和该人员相关的信息,如信箱,文章等等,这样,这些数据库操作语句就构成一个事务!

  • 在 MySQL 中只有使用了 Innodb 数据库引擎的数据库或表才支持事务。
  • 事务处理可以用来维护数据库的完整性,保证成批的 SQL 语句要么全部执行,要么全部不执行。
  • 事务用来管理 insert,update,delete 语句

一般来说,事务是必须满足4个条件(ACID)::原子性(Atomicity,或称不可分割性)、一致性(Consistency)、隔离性(Isolation,又称独立性)、持久性(Durability)。

  • **原子性:**一个事务(transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。
  • **一致性:**在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设规则,这包含资料的精确度、串联性以及后续数据库可以自发性地完成预定的工作。
  • **隔离性:**数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括读未提交(Read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(Serializable)。
  • **持久性:**事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。

一般来说,事物是必须满足 4 个条件(ACID)

  • 原子性
  • 一致性
  • 隔离性
  • 持久性

事物:事物就是我们每执行一个操作的时候的 **会话(session)**也就是我到 ATM 那里插卡存钱到退卡这就是一个 Session。

接下来,我用在银行存一百块钱为例:

  • 原子性:我们的每一个事务,都是插入一条数据,都是很小的一个事件。
  • 一致性:类似我们存进去钱或者取出来钱,或者是中间有上面更改,都可以完完全全的展现在我的表上。(不会是,我存进去一百元而这个表没有任何的变动)也就是说,改动是一致的。
  • 隔离性:比方说两个不同的人,我存一百和你存一百是两个不同的事务,我取钱不会影响到你的资产变动,这就是我们所说的隔离性。
  • 持久性:持久性就是我们数据就一直在那了,你没有操作的话,它就不会改变了。

事物用 BEGIN、ROLLBACK、COMMIT 来实现:

BEGIN开始一个事物
ROLLBACK事物回滚
COMMIT事物确认(提交)

在上面我们创建了一个表,然后我们通过:conn=engine.connect() 获取一个连接。接下来我们开始一个事物:start=conn.begin()

扩展

提升数据库连接的安全性,使用嵌套函数。

连接数据库的样板代码
# 连接数据库的样板代码
from sqlalchemy import create_engine,MetaData,Table


def  my_dabase_big():
	def my_database():
		engine = create_engine(
			"mysql+pymysql://root:123456@127.0.0.1:3306/test",# (里面的 root 要填写你的密码),注意:mysql+pymysql 之间不要加空格
			# "mysql + pymysql://root:root@localhost/test",
			max_overflow = 5, # 超过连接池大小之后,外最多可以创建的链接
			pool_size = 10, # 连接池大小
			echo = True, # 调试信息展示
		)
	my_database()
my_dabase_big()

常用数据类型

名称关键词
字段Column、string、integer 都是字段
MetaData是表结构的额外信息
索引Index
Table
操作方法execute ,update, insert, select, delete, join 等

3.8 索引

数据库创建索引能够大大提高系统性能。

第一通过创建唯一的索引,可以确保数据库表中每一行数据的唯一性;
第二可以大大加快数据的检索速度,这也使创建索引的最主要原因;
第三可以加速表和表之间的连接, 特别是在实现数据的参考完整性方面特别有意义;
第四在使用分组和排序子句进行数据检索时,同样可以显著的减少查询中查询中分组和排序的时间
第五通过使用索引,可以在查询的过程中,使用优化隐藏器,提高系统的性能。
test = Table('host',metadata,
            Column('id',Integer(),primary_key=True),
            Column(('ip'),String(255),index=True),
)

PS:比如我们创建了一个 host 表,其中一个是 id 一个是 ip。假如我们存入许多 id 与 ip,其中有 120、127 开头的,然后我假如要查询其中 127 开头的数据,但是查的时候我需要一条一条检索,这也时间速度上就会慢很多。

可是如果,我们在代码中添加 index=True 之后,就会建立索引。这是什么呢?就是当我们要查询 127 开头,它就只查询 127 的索引部分。这样可以加快速度。

但是需要注意的是,只有在这两种数据分类(区分)非常明显的(类似:男/女),你就可以用 indx=True。这个是一种优化手段,这个是你可以写也可以不写。前期,我们不需要写太多 index,index 写多了反而会造成系统性能下降。(这个是在数据库工程师后期,很深思熟虑的考量之后,才会添加这个 index 索引。但慎用)

接下来,我们来创建表结构,上面我们创建了 test 表,接下来我们创建一个 user 表:

"""
# -*- coding: utf-8 -*-
# @Author:AI悦创 @DateTime :2019/9/29  20:13 @Function :功能  Development_tool :PyCharm
# code is far away from bugs with the god animal protecting
    I love animals. They taste delicious.
              ┏┓      ┏┓
            ┏┛┻━━━┛┻┓
            ┃      ☃      ┃
            ┃  ┳┛  ┗┳  ┃
            ┃      ┻      ┃
            ┗━┓      ┏━┛
                ┃      ┗━━━┓
                ┃  神兽保佑    ┣┓
                ┃ 永无BUG!   ┏┛
                ┗┓┓┏━┳┓┏┛
                  ┃┫┫  ┃┫┫
                  ┗┻┛  ┗┻┛
"""
# 连接数据库的样板代码
from sqlalchemy import create_engine, MetaData, Table, engine
from sqlalchemy import Column, String, Integer, DateTime, Boolean


engine = create_engine(
	"mysql+pymysql://root:123456@127.0.0.1:3306/test",# (里面的 root 要填写你的密码),注意:mysql+pymysql 之间不要加空格
	# "mysql + pymysql://root:root@localhost/test",
	max_overflow = 5, # 超过连接池大小之后,外最多可以创建的链接
	pool_size = 10, # 连接池大小
	echo = True, # 调试信息展示
)


metadata = MetaData() # 获得元数据,介绍数据库
# 定义表
user = Table('user', metadata,
             # 数据库表名称,元素据
             Column('id', Integer, primary_key=True, autoincrement=True),
             Column('name', String(10)))
metadata.create_all(enging)  # 创建数据表
2020年输出
"C:\Program Files\Python37\python.exe" D:/daima/pycharm_daima/爬虫大师班/知识点/DataBase/database_2.py
2019-09-29 20:32:31,984 INFO sqlalchemy.engine.base.Engine SHOW VARIABLES LIKE 'sql_mode'
2019-09-29 20:32:31,984 INFO sqlalchemy.engine.base.Engine {}
C:\Program Files\Python37\lib\site-packages\pymysql\cursors.py:170: Warning: (1366, "Incorrect string value: '\\xBB\\xC6\\xBC\\xD2\\xB1\\xA6...' for column 'VARIABLE_VALUE' at row 75")
  result = self._query(query)
[中间大部分省略......]
C:\Program Files\Python37\lib\site-packages\pymysql\cursors.py:170: Warning: (1366, "Incorrect string value: '\\xBB\\xC6\\xBC\\xD2\\xB1\\xA6...' for column 'VARIABLE_VALUE' at row 404")
  result = self._query(query)
C:\Program Files\Python37\lib\site-packages\pymysql\cursors.py:170: Warning: (1366, "Incorrect string value: '\\xBB\\xC6\\xBC\\xD2\\xB1\\xA6...' for column 'VARIABLE_VALUE' at row 455")
  result = self._query(query)
C:\Program Files\Python37\lib\site-packages\pymysql\cursors.py:170: Warning: (1366, "Incorrect string value: '\\xD6\\xD0\\xB9\\xFA\\xB1\\xEA...' for column 'VARIABLE_VALUE' at row 484")
  result = self._query(query)
2019-09-29 20:32:31,990 INFO sqlalchemy.engine.base.Engine SHOW VARIABLES LIKE 'lower_case_table_names'
2019-09-29 20:32:31,990 INFO sqlalchemy.engine.base.Engine {}
2019-09-29 20:32:31,993 INFO sqlalchemy.engine.base.Engine SELECT DATABASE()
2019-09-29 20:32:31,993 INFO sqlalchemy.engine.base.Engine {}
2019-09-29 20:32:31,994 INFO sqlalchemy.engine.base.Engine show collation where `Charset` = 'utf8mb4' and `Collation` = 'utf8mb4_bin'
2019-09-29 20:32:31,994 INFO sqlalchemy.engine.base.Engine {}
2019-09-29 20:32:31,996 INFO sqlalchemy.engine.base.Engine SELECT CAST('test plain returns' AS CHAR(60)) AS anon_1
2019-09-29 20:32:31,996 INFO sqlalchemy.engine.base.Engine {}
2019-09-29 20:32:31,996 INFO sqlalchemy.engine.base.Engine SELECT CAST('test unicode returns' AS CHAR(60)) AS anon_1
2019-09-29 20:32:31,996 INFO sqlalchemy.engine.base.Engine {}
2019-09-29 20:32:31,997 INFO sqlalchemy.engine.base.Engine SELECT CAST('test collated returns' AS CHAR CHARACTER SET utf8mb4) COLLATE utf8mb4_bin AS anon_1
2019-09-29 20:32:31,997 INFO sqlalchemy.engine.base.Engine {}
2019-09-29 20:32:31,998 INFO sqlalchemy.engine.base.Engine DESCRIBE `user`
2019-09-29 20:32:31,998 INFO sqlalchemy.engine.base.Engine {}
2019-09-29 20:32:32,000 INFO sqlalchemy.engine.base.Engine ROLLBACK
2019-09-29 20:32:32,002 INFO sqlalchemy.engine.base.Engine 
CREATE TABLE user (
	id INTEGER NOT NULL AUTO_INCREMENT, 
	name VARCHAR(10), 
	PRIMARY KEY (id)
)


2019-09-29 20:32:32,002 INFO sqlalchemy.engine.base.Engine {}
2019-09-29 20:32:32,055 INFO sqlalchemy.engine.base.Engine COMMIT

Process finished with exit code 0

PS:里面运行结果如果出现警告,这忽略就好编码问题,上古遗留问题。

注意:里面输出结果有一个语句,我们来看看。

CREATE TABLE user (
	id INTEGER NOT NULL AUTO_INCREMENT, 
	name VARCHAR(10), 
	PRIMARY KEY (id)
)

我们的代码被转换成最原生的数据库代码,是可以直接使用的。

那我们在命令行中查看,表是否真的成功创建:

mysql> show tables;
+----------------+
| Tables_in_test |
+----------------+
| test           |
| user           |
+----------------+
2 rows in set (0.00 sec)

mysql>

# 零基础小白关怀
# 如果你是已经退出或者不知道为什么 show tables;结果不太对,那关闭数据库,按下面步骤操作查询:
(1)mysql -u root -p123456
(2)use test;
(3)show tables;
# 操作过程演示:
clela@黄家宝 D:\文字课程\GitChat 付费课程\Python 之站在高层框架下的SQLAIchemy 操作 MySql
$ mysql -u root -p123456
mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 11
Server version: 5.7.23 MySQL Community Server (GPL)

Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> use test;
Database changed
mysql> show tables;
+----------------+
| Tables_in_test |
+----------------+
| test           |
| user           |
+----------------+
2 rows in set (0.00 sec)

mysql>
# 输出结果和上面一样
mysql> show tables;
+----------------+
| Tables_in_test |
+----------------+
| test           |
| user           |
+----------------+
2 rows in set (0.00 sec)

mysql>

我们还可以再看看,这个表的结构:

# 我们顺便看看, user 表的数据结构:
mysql> desc user;
+-------+-------------+------+-----+---------+----------------+
| Field | Type        | Null | Key | Default | Extra          |
+-------+-------------+------+-----+---------+----------------+
| id    | int(11)     | NO   | PRI | NULL    | auto_increment |
| name  | varchar(10) | YES  |     | NULL    |                |
+-------+-------------+------+-----+---------+----------------+
2 rows in set (0.01 sec)

mysql>

PRI:主键 >>> primary_key=True

我们先快速回顾一下,数据库命令行关键语句:

# 显示数据库
show databases;

# 使用数据库
use test;
# (test:你的数据库名称)

# 查看表结构
desc user;
# (user:你的表名称)

# 查询语句
select * from user_table;
# (user_table:你的数据库表名称)

# 查看该数据库所有表
show tables;

3.9 增加数据(原生语句)方法一

示例代码
# 连接数据库的样板代码
from sqlalchemy import create_engine,MetaData,Table,engine
from sqlalchemy import Column,String,Integer,DateTime,Boolean


engine = create_engine(
	"mysql+pymysql://root:123456@127.0.0.1:3306/test",# (里面的 root 要填写你的密码),注意:mysql+pymysql 之间不要加空格
	# "mysql + pymysql://root:root@localhost/test",
	max_overflow = 5, # 超过连接池大小之后,外最多可以创建的链接
	pool_size = 10, # 连接池大小
	echo = True, # 调试信息展示
)
engine.execute("insert into user (name) values ('lyy')")
engine.execute("insert into user (name) values ('AI悦创')")
# engine.execute("insert into 表名 (字段名) values (值)")

# 查看插入结果
mysql> select * from user;
+----+--------+
| id | name   |
+----+--------+
|  1 | lyy    |
|  2 | AI悦创  |
+----+--------+
4 rows in set (0.00 sec)

mysql>

3.5 利用表结构增删改查——方法二

利用封装好的方法,避免写复杂的底层的 mysql 语句

为了让你们更清晰,我们把上面的 user 表删除。重新创建一个 user_table

我先看一下我们数据库中,有什么表:

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| bool               |
| choose             |
| lesson             |
| look               |
| look_1             |
| mysql              |
| p                  |
| performance_schema |
| pyspider           |
| python_2019        |
| runoob             |
| sys                |
| test               |
| web_info           |
+--------------------+
15 rows in set (0.00 sec)

mysql> use test;
Database changed
mysql> show tables;
Empty set (0.00 sec)

mysql>

我们可以看到,我们 test 数据库下面没有表,接下来我们来创建表。我们来创建一个 user_table

创建 user_table
from sqlalchemy import create_engine,MetaData,Table,engine
from sqlalchemy import Column,String,Integer


engine = create_engine(
	"mysql+pymysql://root:123456@127.0.0.1:3306/test",# (里面的 root 要填写你的密码),注意:mysql+pymysql 之间不要加空格
	# "mysql + pymysql://root:root@localhost/test",
	max_overflow = 5, # 超过连接池大小之后,外最多可以创建的链接
	pool_size = 10, # 连接池大小
	echo = True, # 调试信息展示
)

metadata = MetaData() # 获得元数据,介绍数据库

# 定义表
user_table = Table('user_table', metadata,
                   Column("id", Integer, primary_key=True, autoincrement=True),
                   Column("教学表", String(10)))
metadata.create_all(engine)  # 创建表

# 运行代码的结果
"C:\Program Files\Python37\python.exe" D:/daima/pycharm_daima/爬虫大师班/知识点/DataBase/database_num_4.py
2019-09-30 20:42:46,589 INFO sqlalchemy.engine.base.Engine SHOW VARIABLES LIKE 'sql_mode'
2019-09-30 20:42:46,589 INFO sqlalchemy.engine.base.Engine {}
C:\Program Files\Python37\lib\site-packages\pymysql\cursors.py:170: Warning: (1366, "Incorrect string value: '\\xBB\\xC6\\xBC\\xD2\\xB1\\xA6...' for column 'VARIABLE_VALUE' at row 75")
  result = self._query(query)
C:\Program Files\Python37\lib\site-packages\pymysql\cursors.py:170: Warning: (1366, "Incorrect string value: '\\xBB\\xC6\\xBC\\xD2\\xB1\\xA6' for column 'VARIABLE_VALUE' at row 94")
  result = self._query(query)
C:\Program Files\Python37\lib\site-packages\pymysql\cursors.py:170: Warning: (1366, "Incorrect string value: '\\xBB\\xC6\\xBC\\xD2\\xB1\\xA6...' for column 'VARIABLE_VALUE' at row 259")
  result = self._query(query)
C:\Program Files\Python37\lib\site-packages\pymysql\cursors.py:170: Warning: (1366, "Incorrect string value: '\\xBB\\xC6\\xBC\\xD2\\xB1\\xA6...' for column 'VARIABLE_VALUE' at row 377")
  result = self._query(query)
C:\Program Files\Python37\lib\site-packages\pymysql\cursors.py:170: Warning: (1366, "Incorrect string value: '\\xBB\\xC6\\xBC\\xD2\\xB1\\xA6...' for column 'VARIABLE_VALUE' at row 403")
  result = self._query(query)
C:\Program Files\Python37\lib\site-packages\pymysql\cursors.py:170: Warning: (1366, "Incorrect string value: '\\xBB\\xC6\\xBC\\xD2\\xB1\\xA6...' for column 'VARIABLE_VALUE' at row 404")
  result = self._query(query)
C:\Program Files\Python37\lib\site-packages\pymysql\cursors.py:170: Warning: (1366, "Incorrect string value: '\\xBB\\xC6\\xBC\\xD2\\xB1\\xA6...' for column 'VARIABLE_VALUE' at row 455")
  result = self._query(query)
C:\Program Files\Python37\lib\site-packages\pymysql\cursors.py:170: Warning: (1366, "Incorrect string value: '\\xD6\\xD0\\xB9\\xFA\\xB1\\xEA...' for column 'VARIABLE_VALUE' at row 484")
  result = self._query(query)
2019-09-30 20:42:46,594 INFO sqlalchemy.engine.base.Engine SHOW VARIABLES LIKE 'lower_case_table_names'
2019-09-30 20:42:46,594 INFO sqlalchemy.engine.base.Engine {}
2019-09-30 20:42:46,597 INFO sqlalchemy.engine.base.Engine SELECT DATABASE()
2019-09-30 20:42:46,597 INFO sqlalchemy.engine.base.Engine {}
2019-09-30 20:42:46,597 INFO sqlalchemy.engine.base.Engine show collation where `Charset` = 'utf8mb4' and `Collation` = 'utf8mb4_bin'
2019-09-30 20:42:46,597 INFO sqlalchemy.engine.base.Engine {}
2019-09-30 20:42:46,599 INFO sqlalchemy.engine.base.Engine SELECT CAST('test plain returns' AS CHAR(60)) AS anon_1
2019-09-30 20:42:46,599 INFO sqlalchemy.engine.base.Engine {}
2019-09-30 20:42:46,599 INFO sqlalchemy.engine.base.Engine SELECT CAST('test unicode returns' AS CHAR(60)) AS anon_1
2019-09-30 20:42:46,599 INFO sqlalchemy.engine.base.Engine {}
2019-09-30 20:42:46,600 INFO sqlalchemy.engine.base.Engine SELECT CAST('test collated returns' AS CHAR CHARACTER SET utf8mb4) COLLATE utf8mb4_bin AS anon_1
2019-09-30 20:42:46,600 INFO sqlalchemy.engine.base.Engine {}
2019-09-30 20:42:46,601 INFO sqlalchemy.engine.base.Engine DESCRIBE `user_table`
2019-09-30 20:42:46,601 INFO sqlalchemy.engine.base.Engine {}
2019-09-30 20:42:46,602 INFO sqlalchemy.engine.base.Engine ROLLBACK
2019-09-30 20:42:46,605 INFO sqlalchemy.engine.base.Engine 
CREATE TABLE user_table (
	id INTEGER NOT NULL AUTO_INCREMENT, 
	`教学表` VARCHAR(10), 
	PRIMARY KEY (id)
)


2019-09-30 20:42:46,605 INFO sqlalchemy.engine.base.Engine {}
2019-09-30 20:42:46,650 INFO sqlalchemy.engine.base.Engine COMMIT

Process finished with exit code 0

# 运行之后的数据库结果
mysql> use test;
Database changed
mysql> show tables;
+----------------+
| Tables_in_test |
+----------------+
| user_table     |
+----------------+
1 row in set (0.00 sec)

mysql>

3.6 集成 ORM 类操纵数据——方法三(推荐学会)

更多请查看:https://docs.sqlalchemy.org/en/14/orm/tutorial.htmlopen in new window

from sqlalchemy import create_engine,Column,String,Integer
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import  declarative_base # 导入我们的基类,就是我们创建的类都是以他为基础
# sessionmaker 用于代替 conn 也就是 engine.connect()
# conn = engine.connect() # 获取一个连接
# 连接数据库
engine = create_engine(
	"mysql+pymysql://root:123456@127.0.0.1:3306/test",# (里面的 root 要填写你的密码),注意:mysql+pymysql 之间不要加空格
	# "mysql + pymysql://root:root@localhost/test",
	max_overflow = 5, # 超过连接池大小之后,外最多可以创建的链接
	pool_size = 10, # 连接池大小
	echo = True, # 调试信息展示
)
Base = declarative_base()
class Host(Base):
	# 表名为 hosts
	__tablename__='hosts'
	# 表结构
	# primary_key 等于主键
	# unique 唯一
	# unllable 非空
	id = Column(Integer,primary_key=True,autoincrement=True)
	# unique 类似于微博后端或者其他名称不能重复的操作,也是实现增量式爬虫
	# nullable 有些类似:用户搞怪,在注册页面填了空字符,写这个就是不允许为空
	hostname = Column(String(64),unique=True,nullable=False)
	ip_addr = Column(String(128),unique=True,nullable=False)
	port = Column(Integer, default=22)
	title = Column(String(200))


Base.metadata.create_all(engine) # 创建表

首先用 sessionmaker 绑定 engine,之后实例化绑定 session,用 session 进行 CURD >>> Create、Update、Read、Delete

接下来就是增删改查咯:

# -*- coding: utf-8 -*-
# 写代码是热爱,写到世界充满爱!

from sqlalchemy import create_engine, Column, String, Integer
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base  # 导入我们的基类,就是我们创建的类都是以他为基础

# sessionmaker 用于代替 conn 也就是 engine.connect()
# conn = engine.connect() # 获取一个连接
# 连接数据库
engine = create_engine(
    "mysql+pymysql://root:123456@127.0.0.1:3306/test",  # (里面的 root 要填写你的密码),注意:mysql+pymysql 之间不要加空格
    # "mysql + pymysql://root:root@localhost/test",
    max_overflow=5,  # 超过连接池大小之后,外最多可以创建的链接
    pool_size=10,  # 连接池大小
    echo=True,  # 调试信息展示
)
Base = declarative_base()


class Host(Base):
    # 表名为 hosts
    __tablename__ = 'hosts'
    # 表结构
    # primary_key 等于主键
    # unique 唯一
    # unllable 非空
    id = Column(Integer, primary_key=True, autoincrement=True)
    # unique 类似于微博后端或者其他名称不能重复的操作,也是实现增量式爬虫
    # nullable 有些类似:用户搞怪,在注册页面填了空字符,写这个就是不允许为空
    hostname = Column(String(64), unique=True, nullable=False)
    ip_addr = Column(String(128), unique=True, nullable=False)
    port = Column(Integer, default=22)


# 表创建完成之后就注释掉
# Base.metadata.create_all(engine) # 创建表

if __name__ == '__main__':
    try:
        Session = sessionmaker(bind=engine)
        sess = Session()  # 创建实例
        h = Host(hostname='test1', ip_addr="127.0.0.1")
        h2 = Host(hostname='test2', ip_addr="192.168.0.1", port=8000)
        h3 = Host(hostname='test3', ip_addr="192.168.1.1", port=8080)
        sess.add(h)  # 每次添加一个
        sess.add_all([h2, h3])  # 每次添加多个
        sess.commit()  # 事物提交,必`须要有
    except:
        print("Error!")
if __name__ == '__main__':
	try:
		Session = sessionmaker(bind=engine)
		sess = Session() # 创建实例
		h = Host(hostname='test1',ip_addr="127.0.0.1")
		h2 = Host(hostname='test2',ip_addr="192.168.0.1",port=8000)
		h3= Host(hostname='test3',ip_addr="192.168.1.1",port=8080)
		sess.add(h) # 每次添加一个
		sess.add_all([h2,h3]) # 每次添加多个
		# 更新
		sess.query(Host).filter(Host.id==1).update({"port":2019})
		# Host 创建表的类名,filter 过滤(Host.id==1)
		# 删除
		sess.query(Host).filter(Host.id==1).delete()
		# 查询
		res = sess.query(Host).filter_by(id=1).all()
        # res = sess.query(Host).filter(Host.id==1).all()
		# filter_by(id==1) 过滤,从Host表
		for r in res:
			print(r.hostname)
			print(r.ip_addr)
			print(r.port)
		sess.commit() # 事物提交,必`须要有
	except:
		print("Error!")

四、关系型数据库进阶

4.1 一对多

设计数据库表,程序员网站

  • 单表:一张大表
  • 多表:程序员表和程序员使用语言表
ID-intName-strGender-strFrom-str
Advantage-strDisadvantage-strDateTime-datetime.datetimeCodeName-str

其实,如果实际开发当中,当每个程序员进入该网站(注册该网站),如果每个程序员一注册就创建一张独立的数据库表,这样的数据库冗余太多。

为什么这么讲呢?

因为,假如很多程序员都在用 Python 语言写代码,也就是上面表的 CodeName 那这样就会不断的重复(重复出现 Python 。那我们为何不把重复且不变得数据,单独存一张表呢?使用时直接调用就好。而变化得例如:程序员姓名、年龄、工龄等来创建一张表。

而且,大部分程序员会的编程语言的很多的,也就是说:一个程序员对应多门语言。

例如这样:

在这里插入图片描述
在这里插入图片描述

4.1.1 建表

在这里插入图片描述
在这里插入图片描述

PS:如果上节课得代码出现编码问题,同学们可以添加 charset=UTF8MB4

在这里插入图片描述
在这里插入图片描述
# -*- coding: utf-8 -*-
# 写代码是热爱,写到世界充满爱!
# @Author:AI悦创 @DateTime :2019/10/1  16:58 @Function :功能  Development_tool :PyCharm

from sqlalchemy import create_engine,Column,Integer,String,ForeignKey
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker,relationship
# 建立一对多的方法,必须使用 relationship

# 连接数据库
engine = create_engine(
	"mysql+pymysql://root:123456@127.0.0.1:3306/test?charset=UTF8MB4",# (里面的 root 要填写你的密码),注意:mysql+pymysql 之间不要加空格
	# "mysql + pymysql://root:root@localhost/test",
	max_overflow = 5, # 超过连接池大小之后,外最多可以创建的链接
	pool_size = 10, # 连接池大小
	echo = True, # 调试信息展示
)

Base = declarative_base()
# <-----------------user_table----------------->
class User(Base):
	__tablename__="user"
	# 表结构
	# primary_key 等于主键
	# unique 唯一
	# nullable 非空
	# nullable = False : 不允许为空
	# nullable = True  : 允许为空
	id = Column(Integer(), primary_key=True, autoincrement=True)
	name = Column(String(125), nullable=True)
	advantage = Column(String(125), nullable=True)
	disadvantage = Column(String(125), nullable=True)
	town = Column(String(125),nullable=True)
	language = relationship("Language", backref="user")

class Language(Base):
	__tablename__="language"
	id = Column(Integer(), primary_key=True, autoincrement=True)
	name = Column(String(125),nullable=True)
	advantage = Column(String(125), nullable=True)
	disadvantage = Column(String(125), nullable=True)
	user_id = Column(Integer(), ForeignKey("user.id"))
# 在添加数据之前,我们得先创建表
Base.metadata.create_all(engine)

创建表之后,命令行查看:

mysql> use test;
Database changed
mysql> desc user;
+--------------+--------------+------+-----+---------+----------------+
| Field        | Type         | Null | Key | Default | Extra          |
+--------------+--------------+------+-----+---------+----------------+
| id           | int(11)      | NO   | PRI | NULL    | auto_increment |
| name         | varchar(125) | YES  |     | NULL    |                |
| gender       | varchar(125) | YES  |     | NULL    |                |
| advantage    | varchar(125) | YES  |     | NULL    |                |
| disadvantage | varchar(125) | YES  |     | NULL    |                |
| town         | varchar(125) | YES  |     | NULL    |                |
+--------------+--------------+------+-----+---------+----------------+
6 rows in set (0.00 sec)

mysql> desc language;
+--------------+--------------+------+-----+---------+----------------+
| Field        | Type         | Null | Key | Default | Extra          |
+--------------+--------------+------+-----+---------+----------------+
| id           | int(11)      | NO   | PRI | NULL    | auto_increment |
| name         | varchar(125) | YES  |     | NULL    |                |
| advantage    | varchar(125) | YES  |     | NULL    |                |
| disadvantage | varchar(125) | YES  |     | NULL    |                |
| user_id      | int(11)      | YES  | MUL | NULL    |                |
+--------------+--------------+------+-----+---------+----------------+
5 rows in set (0.00 sec)

mysql> select * from user;
+----+------+--------+-----------+--------------+------+
| id | name | gender | advantage | disadvantage | town |
+----+------+--------+-----------+--------------+------+
|  1 | 张三 || NULL      | NULL         | 北京  |
|  2 | 李四 || NULL      | NULL         | 天津  |
+----+------+--------+-----------+--------------+------+
2 rows in set (0.00 sec)

mysql> select * from language;
+----+--------+-----------+--------------+---------+
| id | name   | advantage | disadvantage | user_id |
+----+--------+-----------+--------------+---------+
|  1 | Python | 开发快     | 运行慢        |       1 |
+----+--------+-----------+--------------+---------+
1 row in set (0.00 sec)

mysql>
# -*- coding: utf-8 -*-
# 写代码是热爱,写到世界充满爱!
# @Author:AI悦创 @DateTime :2019/10/1  16:58 @Function :功能  Development_tool :PyCharm

from sqlalchemy import create_engine,Column,Integer,String,ForeignKey
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker,relationship
# 建立一对多的方法,必须使用 relationship

# 连接数据库
engine = create_engine(
	"mysql+pymysql://root:123456@127.0.0.1:3306/test",# (里面的 root 要填写你的密码),注意:mysql+pymysql 之间不要加空格
	# "mysql + pymysql://root:root@localhost/test",
	max_overflow = 5, # 超过连接池大小之后,外最多可以创建的链接
	pool_size = 10, # 连接池大小
	echo = True, # 调试信息展示
)

Base = declarative_base()
# <-----------------user_table----------------->
class User(Base):
	__tablename__="user"
	# 表结构
	# primary_key 等于主键
	# unique 唯一
	# nullable 非空
	# nullable = False : 不允许为空
	# nullable = True  : 允许为空
	id = Column(Integer(), primary_key=True, autoincrement=True)
	name = Column(String(125), nullable=True)
	gender = Column(String(125),nullable=True)
	advantage = Column(String(125), nullable=True)
	disadvantage = Column(String(125), nullable=True)
	town = Column(String(125),nullable=True)
	# relationship :n. 关系;关联
	# 这行代码:可有可无
	language = relationship("Language", backref="user")
# 	backref : 也就是背面,允许 language 表直接映射,访问 user 表中的数据

# <-----------------language_table----------------->
class Language(Base):
	__tablename__="language"
	id = Column(Integer(), primary_key=True, autoincrement=True)
	name = Column(String(125),nullable=True)
	advantage = Column(String(125), nullable=True)
	disadvantage = Column(String(125), nullable=True)
	# 添加 user_id 数据,然后写它的外键是 user 里面的 id 表
	user_id = Column(Integer(), ForeignKey("user.id"))


# 在添加数据之前,我们得先创建表
# Base.metadata.create_all(engine)

# <-----------------add_data----------------->
if __name__ == '__main__':
# <---------------方法一--------------->
	# 添加数据方法一
	Session = sessionmaker(engine)
	session = Session()
# 	添加用户
	user1 = User(name='张三', gender="男", town="北京")
	user2 = User(name='李四', gender="女", town="天津")
	session.add_all([user1, user2])
	session.commit()
# 	添加语言
	language1 = Language(name="Python", advantage="开发快", disadvantage="运行慢")
	# language2 = Language(name="C++", advantage="开发", disadvantage="测试")
	language1.user = user1
	session.add(language1)
	session.commit()
# <---------------方法二--------------->
# 	添加数据方法二(同时添加)
	Session = sessionmaker(engine)
	session = Session()
	user1 = User(name="AI悦创", gender="男",advantage="Python", disadvantage="Run_low" )
	user1.language = [
		Language(name="Python", advantage="开发快", disadvantage="运行慢"),
		Language(name="C", advantage="开发慢", disadvantage="运行快")
	]
	session.add(user1)
	session.commit()

4.1.2 查找数据

if __name__ == '__main__':
	Session = sessionmaker(engine)
	session = Session()
	user_select = session.query(User).filter_by(id=3).first()
	print("name:>>>", user_select.name)
	lan = session.query(Language).filter_by(user_id=user_select.id)
	for i in lan:
		print("language:>>>",i.name)

查询数据得到的是列表。要全部数据就是 all(),第一个数据就是 first()

4.1.3 删除数据

删除数据的时候,如果有关联数据,关联数据空下的字段会变成 NULL,浪费空间。

if __name__ == '__main__':
	Session = sessionmaker(engine)
	session = Session()
	use = session.query(User).filter(User.id==3).first()
	session.delete(use)
	session.commit()

删除之前:

mysql> select * from user;
+----+--------+--------+-----------+--------------+------+
| id | name   | gender | advantage | disadvantage | town |
+----+--------+--------+-----------+--------------+------+
|  1 | 张三   || NULL      | NULL         | 北京 |
|  2 | 李四   || NULL      | NULL         | 天津 |
|  3 | AI悦创 || Python    | Run_low      | NULL |
+----+--------+--------+-----------+--------------+------+
3 rows in set (0.00 sec)

mysql> select * from language;
+----+--------+-----------+--------------+---------+
| id | name   | advantage | disadvantage | user_id |
+----+--------+-----------+--------------+---------+
|  1 | Python | 开发快    | 运行慢       |       1 |
|  2 | Python | 开发快    | 运行慢       |       3 |
|  3 | C      | 开发慢    | 运行快       |       3 |
+----+--------+-----------+--------------+---------+
3 rows in set (0.00 sec)

mysql>

删除之后:

mysql> select * from user;
+----+------+--------+-----------+--------------+------+
| id | name | gender | advantage | disadvantage | town |
+----+------+--------+-----------+--------------+------+
|  1 | 张三 || NULL      | NULL         | 北京 |
|  2 | 李四 || NULL      | NULL         | 天津 |
+----+------+--------+-----------+--------------+------+
2 rows in set (0.00 sec)

mysql> select * from language;
+----+--------+-----------+--------------+---------+
| id | name   | advantage | disadvantage | user_id |
+----+--------+-----------+--------------+---------+
|  1 | Python | 开发快    | 运行慢       |       1 |
|  2 | Python | 开发快    | 运行慢       |    NULL |
|  3 | C      | 开发慢    | 运行快       |    NULL |
+----+--------+-----------+--------------+---------+
3 rows in set (0.00 sec)

mysql>

为了让你更清晰,我把它截图下来:

在这里插入图片描述
在这里插入图片描述

可以知道,这样删除数据会有数据冗余,那我们该如何操作呢?

在这里插入图片描述
在这里插入图片描述

这时候,你会发现。我们只加这一句:

Base.metadata.drop_all(engine)

但是,如果不想删除,我在这里教你在命令行重新创建一个数据库:

# 比如我们创建一个 test2 的数据库
# 我们先查看已经有哪些数据库
# 不过在这之前我们需要,运行 mysql
mysql -u root -p
passowrd:****
# 上面 -u 后面需要写上你自己数据库用户名称,回车之后就输入数据库密码即可。
# 当然还可以这样登录:
mysql -u root -p123456
# 123456 直接写上你的密码
# 进入之后,查看有哪些数据库:
show databases;
# 创建数据库,不能与已有的数据库重名
CREATE DATABASE 数据库名称;
# 当然,小写也是可以的
create database 数据库名称;
# 创建成功就会给你返回:
Query OK, 1 row affected (0.00 sec)

# 删除表就是 
drop table;
# 同时删除多个
drop table1 table;
# 如果有关联,那就先删除它的关联表(自己试一下就知道了)
# Ps: 如果要在代码中删除,那就写
drop_all()
# 但不建议在代码中写删除,太危险了

截至目前操作(包含创建表和删除表):

from sqlalchemy import create_engine,Column,Integer,String,ForeignKey
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker,relationship
# 建立一对多的方法,必须使用 relationship

# 连接数据库
engine = create_engine(
	"mysql+pymysql://root:123456@127.0.0.1:3306/test",# (里面的 root 要填写你的密码),注意:mysql+pymysql 之间不要加空格
	# "mysql + pymysql://root:root@localhost/test",
	max_overflow = 5, # 超过连接池大小之后,外最多可以创建的链接
	pool_size = 10, # 连接池大小
	echo = True, # 调试信息展示
)

Base = declarative_base()
# <-----------------user_table----------------->
class User(Base):
	__tablename__="user"
	# 表结构
	# primary_key 等于主键
	# unique 唯一
	# nullable 非空
	# nullable = False : 不允许为空
	# nullable = True  : 允许为空
	id = Column(Integer(), primary_key=True, autoincrement=True)
	name = Column(String(125), nullable=True)
	gender = Column(String(125),nullable=True)
	advantage = Column(String(125), nullable=True)
	disadvantage = Column(String(125), nullable=True)
	town = Column(String(125),nullable=True)
	# relationship :n. 关系;关联
	# 这行代码:可有可无
	language = relationship("Language", backref="user", cascade='all,delete')
# 	backref : 也就是背面,允许 language 表直接映射,访问 user 表中的数据

# <-----------------language_table----------------->
class Language(Base):
	__tablename__="language"
	id = Column(Integer(), primary_key=True, autoincrement=True)
	name = Column(String(125),nullable=True)
	advantage = Column(String(125), nullable=True)
	disadvantage = Column(String(125), nullable=True)
	# 添加 user_id 数据,然后写它的外键是 user 里面的 id 表
	# 也就是关联操作
	user_id = Column(Integer(), ForeignKey("user.id"))

# 如果要删除表,按下面操作:
Base.metadata.drop_all(engine)
# 在添加数据之前,我们得先创建表
Base.metadata.create_all(engine)

添加数据,和上面的一样。这回,我们再删除数据。这样就成功咯!

突然想到这句话:删库跑路

4.1.4 更新数据

if __name__ == '__main__':
	Session = sessionmaker(engine)
	session = Session()
	u = session.query(User).filter(User.id==1).first()
	u.name = "赵六"
	session.commit()
在这里插入图片描述
在这里插入图片描述
if __name__ == '__main__':
	Session = sessionmaker(engine)
	session = Session()
	u = session.query(User).filter(User.id==4).first()
	lan = u.language[0].name='python3'
	session.commit()

4.1.5 事物回滚

在这里插入图片描述
在这里插入图片描述

事物回滚,为什么需要这样的操作呢?

就例如:在我们天猫抢单时(虽然我没抢过),但是,如果你抢单失败,其实在代码中就类似于,你的代码突然运行错误,那这时候就需要恢复之前的操作,比如:选择商品、商品的属性、商品件数、促销价格等等。这些操作,其实是在操作数据库里面的数据,而此时,你没有抢单成功,就需要回滚你原本的操作。

不然,你实际是没有购买到,但数据库里面的记录是显示你有操作购买,那样数据库中的数据不完整了。所以,为了保证数据库中的数据完整性,则需要回滚操作。恢复之前的操作,也就是恢复之前对数据库的操作。

所以使用 try...except...

也就是说,在 session.commit() 之前(也就是提交数据之前),修改的数据类似于在内存缓冲区里面,执行回滚操作就是清楚缓冲区所修改的数据。一般都是会加上这个机制。

最后

到此本节教程基本结束了,十分感谢您的观看,由于内容及文字过多,为了帮您更好地理清思路,提高阅读效果,以下是本篇的总结。

  1. Python 语法快速入门,语言是基础
  2. 数据库的简单语句操作,已经使用 Python 语法对 MySQL 数据库操作
  3. 掌握 SQLAlchemy 的语法与使用
  4. 进阶涉及多对多表连接等,简单涉及一部分
公众号:AI悦创【二维码】

AI悦创·编程一对一

AI悦创·推出辅导班啦,包括「Python 语言辅导班、C++ 辅导班、java 辅导班、算法/数据结构辅导班、少儿编程、pygame 游戏开发、Linux、Web全栈」,全部都是一对一教学:一对一辅导 + 一对一答疑 + 布置作业 + 项目实践等。当然,还有线下线上摄影课程、Photoshop、Premiere 一对一教学、QQ、微信在线,随时响应!微信:Jiabcdefh

C++ 信息奥赛题解,长期更新!长期招收一对一中小学信息奥赛集训,莆田、厦门地区有机会线下上门,其他地区线上。微信:Jiabcdefh

方法一:QQopen in new window

方法二:微信:Jiabcdefh

上次编辑于:
贡献者: AndersonHJB,AI悦创
你认为这篇文章怎么样?
  • 0
  • 0
  • 0
  • 0
  • 0
  • 0
评论
  • 按正序
  • 按倒序
  • 按热度