4-Flask数据库
4-1数据库的介绍
为什么要学习数据库
通常,我们存储数据,直接用本地文件即可。但是,本地文件不利于存放海量数据,也不利于用程序对文件的数据进行查询与管理。那么为了解决这些弊端,才有数据库的出现,那么数据库也是每个程序员必须掌握的技术。
数据库的介绍
数据库(database)简称DB,实际上就是一个文件集合,是一个存储数据的仓库,本质就是一个文件系统,数据库是按照特定的格式把数据存储起来,用户可以对存储的数据进行增删改查等操作。
数据库的优点
- 持久化存储
- 读写速度极高
- 保证数据的有效性
- 对程序支持非常好,容易扩展
数据库的分类
- 关系型数据库:可以保存现实生活中的各种关系数据,数据库中存储数据以表为单位;主流关系型数据库:MySQL,Oracle,SQLServer等
- 非关系型数据库:通常用来解决某些特定的需求,比如高并发访问。主流非关系型数据库:Redis,Mongodb,memacache等
4-2 MySQL介绍与安装
MySQL介绍
MySQL是一种开放源代码的关系型数据库管理系统(RDBMS),使用最常用的数据库管理语言——结构化查询语言(SQL)进行数据库管理。MySQL因为其速度、可靠性和适应性而备受关注。大多数人都认为在不需要事务化处理的情况下,MySQL是管理内容最好的选择。
MySQL安装
下载地址:http://www.mysql.com/downloads
下载之后可参考:https://jingyan.baidu.com/article/0aa223751ed91188cc0d643f.html进行安装
4-3 语法基础
SQL简介
SQL是结构化查询语言,是一种用来操作RDBMS(关系型数据库管理系统)的数据库语言,当前关系型数据库都支持使用SQL语言进行操作,也就是说可以通过SQL操作oracle,sql server,mysql等关系型数据库。
SQL语句主要分为
- DQL:数据查询语言,用于对数据进行查询
- DML:数据操作语言,对数据进行增加、修改和删除
- DDL:数据定义语言,进行数据库、表的管理等
数据完整性
在表中为了更加准确的存储数据,保证数据的正确有效,可以在创建表的时候,为表添加一些强制性的验证,包括数据字段的类型、约束。
数据类型
- 整型:
int
- 小数:
decimal
- 字符串:
varchar、char
- 日期时间:
datetime、time、data
- 枚举型:
enum
Number类型
Text类型
Date类型
在这里插入图片描述
注意
- decimal表示浮点数,如decimal(5,2)表示共存5位数,小数占2位
- char表示固定长度,如char(3),如果填充
ab
时会补上一个空格’ab ’ - varchar表示可变长度的字符串,如varchar(3),填充
ab
时会存储’ab’ - 字符串text表示存储大文本,当字符大于4000时推荐使用
- 对于图片、音频、视频等文件,不存储在数据库中,二手上传到某个服务器上,然后在表中存储这个文件的保存路径
- MySQL数据库下标起始点从1开始
约束
- 主键primary key:物理上存储的顺序
- 非空not null:此字段不允许填写空值
- 惟一unique:此字段的值不允许重复
- 默认default:当不填写此值时会使用默认值,如果填写时以填写为准
- 外键foreign key:对关系字段进行约束,当为关系字段填写值时,会到关联的表中查询此值是否存-在,如果存在则填写成功,如果不存在则填写失败并抛出异常。
- 自增auto_increment:自增
4-4 SQL语法
操作数据库(DDL )
-
数据库连接
mysql -u 用户名 -p
:密码
-
查看所有的数据库
show databases;
-
创建数据库
- 创建数据库使用的是`create``
- ``create database 要创建的数据库名字;`
-
创建的数据库并指定编码
create database charset=utf-8;
-
查看当前使用的数据库
select database();
-
使用数据库
use 要使用的数据库名字;
-
删除数据库
drop database 数据库名
-
练习
- 创建数据库:Logic_edu
create database Logic_edu;
- 查看已创建的数据库
show databases;
- 查看当前使用的数据库是否是Logic_edu
select database();
- 若不是,则切换使用Logic_edu数据库
use Logic_edu;
- 删除Logic_edu数据库
drop database Logic_edu;
操作数据表
-
查看当前创建的数据表
show tables;
-
创建表
create table student(id int primary key not null auto_increment, name varchar(30), age tinyint defalut 0, genspider enum("男","女","保密")defalut "保密");
create table 要创建的表名(字段,类型,约束[字段 类型 约束]);
-
查看表的创建语句
show create table 数据表名;
-
查看表的描述
desc 数据表名
-
create table student(id int(11) not null primary key auto_increment, name varchar(30), age tinyint default 0, gender enum("男","女","保密") default "保密");
-
添加表字段
alter table 表名 add 字段 类型
-
修改表字段
alter table 表名 modify 字段 类型
– 不重名alter table 表名 change 原字段名 新字段名 类型及约束
– 将字段重名名
-
删除表字段
alter table 表名 drop 字段
操作数据库
-
整行插入
insert into 表名字 values(值...);
-
指定列中插入数据
insert into 表名字(字段1,字段2) values (值1,值2);
-
指定列中插入多条数据
insert into 表名字(字段1,字段2)values (值1,值2),(值1,值2)
-
练习
- Student表中添加整行数据
insert into Student values (0,'SmallJ',18,1);
- Student表中添加name,gender两个字段的数据
insert into Student (name gender) value ('SevenJ' 1)
- Student表中同时添加多行数据
insert into Student values (0,'SmallJ',18,1), (1,'SenveJ',20,1);
-
修改数据
update 数据表名 set 字段1=新值,字段2=新值[where 条件];
-
练习
- 将姓名全部修改为jack
update student set name='jack';
- 将性别为女的名字修改为rose
update student set name='rose' where gender='女';
- 将id为3的年龄修改为22,并且性别改为男
update student set age=22,gender='男' where id=3;
查询数据
- 查询整个表数据
select * from 数据表名;
- 查询指定字段数据
select 字段1,字段2,from 数据表名;
- 查询指定字段数据,并给字段起别名
select 字段1 as 别名,字段2 as 别名 from 数据表名;
- 查询指定字段并去重
select distinct 字段1 from 数据表名;
4-5 SQLAlchemy介绍和基本使用
数据库是一个网站的基础。Flask可以使用很多种数据库。比如MySQL,MongoDB,SQLite,PostgreSQL等。这里我们以MySQL为例进行讲解。而在Flask中,如果想要操作数据库,我们可以使用ORM来操作数据库,使用ORM操作数据库将变得非常简单。
在讲解Flask中的数据库操作之前,先要安装这些模块
- mysql : https://www.mysql.com/
- pymysql : pymysql是用Python来操作mysql的包,因此通过pip来安装
pip install pymysql
- SQLALchemy : SQLALchemy是一个数据库的ORM框架,安装命令 :
pip install SQLALchemy
# create_engine 创建引擎
from sqlalchemy import create_engine
USERNAME = 'root'
PASSWORD = 'root'
DATABASE = 'spider'
HOSTNAME = '127.0.0.1'
PORT = 3306
DB_URL = 'mysql+pymysql://{}:{}@{}:{}/{}?charset=utf8'.format(USERNAME, PASSWORD, HOSTNAME, PORT, DATABASE)
# 创建数据库引擎
engine = create_engine(DB_URL)
with engine.connect() as conn:
# 执行原生的MySQL语句
result = conn.execute('select * from job_data')
print(result.fetchall())
首先从sqlalchemy
中导入create_engine
, 用这个函数来创建引擎,然后用engine.connect()
来连接数据库。其中一个比较重点的一点是,通过create_engine
函数的时候,需要传递一个满足某种格式的字符串,对这个字符串的格式来进行解释
dialect+driver://username:password@host:port/database?charset=utf8
dialect
是数据库的实现,比如MySQL、PostgreSQL、SQLite,并且转换成小写。drive是Python对应的驱动,如果不指定,会选择默认的驱动,比如MySQL的默认驱动是MySQLdb。username是连接数据库的用户名,password是连接数据库的密码,host是连接数据库的域名,port是数据库监听的端口号,database是连接哪一个数据库的名字。
4-6 SQLAlchemy-ORM介绍
ORM介绍
随着项目越来越大,采用原生的SQL的方式在代码中会出现大量的SQL语句,对项目的进展非常不利。
- SQL语句重复利用率不高,越复杂的SQL语句条件越多,代码越长。会出现很多相近似的SQL语句
- 很多SQL语句是在业务逻辑中拼出来的,如果有数据库需要更改,就要去修改这些逻辑,很容易漏掉某些SQL语句的修改
- 写SQL时容易忽略web安全问题
ORM:Object Relationship Mapping
,对象关系映射,通过ORM我们可以通过类的方式去操作数据库,而不是写原生的SQL语句。通过把表映射成类,把行作为实例,把字段作为属性,ORM在执行对象操作时候最终还是会把对应的操作转换为数据库原生语句
使用ORM的优点
- 易用性:使用ORM做数据库的开发可以有效的减少SQL语句,写出来的模型也更加直观
- 性能损耗小
- 设计灵活 : 可以轻松写出来的复杂的查询
- 可移植性 :SQLAlchemy封装了底层的数据库实现,支持多个关系型数据库,还包括MySQL,SQLite
SQLAlchemy会自动的设置第一个Integet的主键并且没有被标记为外键的字段添加自增长的属性。创建完和表映射的类后,还没有真正的映射到数据库当中,执行以下代码将映射到数据库中
Base.metadata.create_all()
# @Time : 2020/4/21 11:53
# @Author : SmallJ
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import *
from sqlalchemy.orm import sessionmaker
DB_URL = 'mysql+pymysql://root:[email protected]:3306/spider'
# 创建引擎
engine = create_engine(DB_URL)
# 实例化对象
Base = declarative_base(engine)
class book_data(Base):
__tablename__ = 'book_data'
id = Column(Integer, primary_key=True, autoincrement=True)
# String必须给长度,ORM会帮我们转换为varchar类型数据
# nullable 当为False时,表示该字段不能为空
book_name = Column(String(30), nullable=False)
book_price = Column(Float(4), nullable=False)
# 创建表
# Base.metadata.create_all()
# 实例化对象传参
data = book_data(book_name='Python数据爬虫', book_price=93.64)
# 需要传递bind绑定引擎来操作数据库
Session = sessionmaker(bind=engine)
# 类的实例化 __call__ 将类变成方法去调用
session = Session()
# 添加数据
session.add(data)
# 提交表单
session.commit()
4-7 SQLAlchemy属性常用数据类型
Column常用参数
方法 | 描述 |
---|---|
defalut | 默认值 |
nullable | 是否可有 |
primark_key | 是否为主键 |
unique | 是否唯一 |
autoincrement | 是否自动增长 |
onupdata | 更新的时候执行的函数 |
name | 该属性在数据库中的字段映射 |
SQLAlchemy常用数据类型
方法 | 描述 |
---|---|
Integer | 整型 |
Float | 浮点类型 |
Boolean | 传递True/False |
DECIMAL | 定点类型 |
enum | 枚举类型 |
Date | 传递daretime.data() |
Time | 传递datatime.time() |
String | 字符类型,使用时需要指定长度,区别于Text类型 |
Text | 文本类型 |
LONGTEXT | 长文本类型 |
query可用参数
- 模型对象。指定查找这个模型中所有的对象
- 模型中的数据。可以指定只查找某个模型的其中几个属性
- 聚合函数
func.count
: 统计行的数量func.avg
: 求平均值func.max
: 求最大值func.min
: 求最小值func.sum
: 求和
过滤条件
过滤条件是数据提取的一个很重要的功能,以下对一些常用的过滤条件进行解释,并且这些过滤条件都是只能通过filter
方法来实现
# @Time : 2020/4/21 12:15
# @Author : SmallJ
from sqlalchemy import *
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
DB_URL = 'mysql+pymysql://root:[email protected]:3306/spider?charset=utf8'
engine = create_engine(DB_URL)
base = declarative_base(engine)
class book_data(base):
__tablename__ = 'book_data'
id = Column(Integer, primary_key=True, autoincrement=True)
book_name = Column(String(30), nullable=False)
book_press = Column(String(30), nullable=False)
book_price = Column(Float(4), nullable=False)
# 调用类的时候执行
def __str__(self):
return 'book_name: {}, book_press: {}, book_price: {} '.format(self.book_name, self.book_press, self.book_price)
# base.metadata.create_all()
Session = sessionmaker(bind=engine)
session = Session()
def add_data():
data2 = book_data(book_name='Python数据分析', book_press='中国南京大学', book_price=107.36)
data1 = book_data(book_name='Python数据爬虫', book_press='中国工业大学', book_price=100.34)
session.add_all([data1, data2])
session.commit()
def search_data():
"""
query 为查询
all 为查询所有, 返回是一个列表的方式
filter 为条件过滤
filter是通过类调用方法来进行条件过滤
filter_by 只用通过方法来进行条件过滤
first 返回第一条数据
get 通过id的值来进行返回数据,当不存在返回None
:return:
"""
# datas = session.query(book_data).all()
# data = session.query(book_data).filter(book_data.book_name == 'PythonWeb开发').all()
# data = session.query(book_data).filter_by(book_name='Python数据爬虫').first()
data = session.query(book_data).get(2)
def update_data():
# 查询出要修改的这条记录
data = session.query(book_data).first()
data.book_name = 'Python机器学习'
# 修改完并没有真的完,需要提交数据
session.commit()
def delete_data():
data = session.query(book_data).first()
session.delete(data)
# 回滚
session.rollback()
session.commit()
if __name__ == '__main__':
# add_data()
# search_data()
update_data()
# @Time : 2020/4/21 16:59
# @Author : SmallJ
from sqlalchemy import *
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from datetime import datetime
from sqlalchemy.dialects.mysql import LONGTEXT
DB_URL = 'mysql+pymysql://root:[email protected]:3306/spider'
# 创建引擎
engine = create_engine(DB_URL)
# 实例化对象
# bind为绑定引擎
base = declarative_base(bind=engine)
class movie_data(base):
__tablename__ = 'movie_data'
id = Column(Integer, primary_key=True, autoincrement=True)
# 字符串必须指定长度
movie_name = Column(String(30), nullable=False)
author = Column(String(30), nullable=False)
release_time = Column(DateTime)
# 是否删除 布尔类型 1代表删除 0代表没有删除
is_delete = Column(Boolean)
# DECIMAL 为定点类型
# 总共有多少位,就保留多少位。
price = Column(DECIMAL(7, 3))
# LONGTEXT 需要单独导入
context = Column(LONGTEXT)
# 删除表
# base.metadata.drop_all()
# 创建表
# base.metadata.create_all()
# print(datetime.now())
Session = sessionmaker(bind=engine)
session = Session()
def add_data():
data1 = movie_data(movie_name='肖申克救赎', author='弗兰克', release_time=datetime.now(), is_delete=False, price=336.345, context='希望让人自由')
session.add(data1)
session.commit()
if __name__ == '__main__':
add_data()
# @Time : 2020/4/21 12:15
# @Author : SmallJ
from sqlalchemy import *
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
DB_URL = 'mysql+pymysql://root:[email protected]:3306/spider?charset=utf8'
engine = create_engine(DB_URL)
base = declarative_base(engine)
class book_data(base):
__tablename__ = 'book_data'
id = Column(Integer, primary_key=True, autoincrement=True)
book_name = Column(String(30), nullable=False)
book_press = Column(String(30), nullable=False)
book_price = Column(Float(4), nullable=False)
# 调用类的时候执行
def __str__(self):
return 'book_name: {}, book_press: {}, book_price: {} '.format(self.book_name, self.book_press, self.book_price)
# base.metadata.create_all()
Session = sessionmaker(bind=engine)
session = Session()
def add_data():
data2 = book_data(book_name='Python数据分析', book_press='中国南京大学', book_price=107.36)
data1 = book_data(book_name='Python数据爬虫', book_press='中国工业大学', book_price=100.34)
session.add_all([data1, data2])
session.commit()
def search_data():
"""
query 为查询
all 为查询所有, 返回是一个列表的方式
filter 为条件过滤
filter是通过类调用方法来进行条件过滤
filter_by 只用通过方法来进行条件过滤
first 返回第一条数据
get 通过id的值来进行返回数据,当不存在返回None
:return:
"""
# datas = session.query(book_data).all()
# data = session.query(book_data).filter(book_data.book_name == 'PythonWeb开发').all()
# data = session.query(book_data).filter_by(book_name='Python数据爬虫').first()
data = session.query(book_data).get(2)
def update_data():
# 查询出要修改的这条记录
# data = session.query(book_data).all()
# for itme in data:
# print(itme)
"""
func.count : 统计行的数量
func.avg : 平均值
func.max : 求最大值
func.sum : 求和
:return:
"""
data = session.query(func.avg(book_data.book_price)).first()
print(data)
# 修改完并没有真的完,需要提交数据
session.commit()
def delete_data():
pass
# data = session.query(book_data).first()
# session.delete(data)
# # 回滚
# session.rollback()
# session.commit()
if __name__ == '__main__':
# add_data()
# search_data()
update_data()