数据库
数据库是大多数动态web程序的基础设施,只要你想把数据存下来,就离不开数据库。
这里所说的数据库指的是有存储数据的单个或多个文件组成的集合,它是一种容器,可以类比文文件柜。而人们通常使用数据库来表示操作数据库的软件,这类管理数据库的软件被称为数据库管理系统,常见的数据库管理系统(DBMS)有MySQL、SQLite、MongoDB等。为了便于理解,我们可以把数据库看作一个大仓库,仓库里有一些负责搬运货物的机器人,而数据库管理系统就是操控机器人搬运货物的程序。
下面我们来学习如何给Flask程序添加数据库支持。具体来说,是如何学习在python中使用这些DBMS来对数据库进行管理和操作。
数据库的分类
数据库一般分为两种,SQL(Structured Query Language,结构化查询语言)数据库和NoSQL(Not Only SQL,泛指非关系型)数据库。
SQL
SQL数据库指关系型数据库,常用的SQL DBMS主要包括SQL Server、Oracle、MySQL、SQLite等。关系型数据库使用表来定义数据对象,不同的表之间使用关系连接。下表是一个身份消息表示例:
在SQL数据库中,每一行代表一条记录,每条记录又由不同的列(column)组成。在存储数据前,需要预定义表模式(schema),以定义表的结构并限定列的输入数据类型。
先来了解几个基本概念:
1) 表(table):存储数据的特定结构
2) 模式(schema):定义表的结构信息。
3) 列/字段(column/field):表中的列,存储一系列特定的数据,列组成表。
4) 行/记录(row/record):表中的行,代表一条记录。
5) 标量(scalar):指的是单一数据,与之相对的是集合(collection)。
NoSQL
NoSQL最初指No SQL或No Relational,现在NoSQL社区一般户解释为Not Only SQL。NoSQL数据库泛指不使用传统关系型数据库中的表格形式的数据库。近年来,NoSQL数据库越来越流行,被大量应用在实时(real-time)Web程序和大型程序中。与传统的SQL数据库相比,它在速度和可扩展性方面有很大的优势,除此之外还拥有无模式(schema-free)、分布式、水平伸缩(horizontally scalable)等特点。
最常用的两种NoSQL数据库如下所示:
文档存储
文档存储时NoSQL数据库中最流行的种类,它可以作为主数据库使用。文档存储使用的文档类似SQL数据库中的记录,文档使用类JSON格式来表示数据。常见的文档存储DBMS有MongoDB、CouchDB等。
前面的身份信息表的第一条记录使用该文档可以表示为:
{
id: 1,
name: "Nick",
sex: "Male"occupation: "Journalist"
}
键值对存储(key-value store)
键值对存储在形态上类似python中的字典,通过键来存储数据,在读取上非常快,通常用来存储临时内容,作为缓存使用。常见的键值对DBMS有Redis、Riak等,其中Redis不仅可以管理键值对数据库,还可以作为缓存后端(cache backend)和消息代理(message broker)。
另外,还有列存储(column store,又被称为宽列式存储)、图存储(graph store)等类型的NoSQL数据库。
如何选择?
NoSQL数据库不需要定义表和列等结构,也不限定存储的数据格式,在存储方式上比较灵活,在特定的场景下效率更高。SQL数据库稍显复杂,但不容易出错,能够适应大部分的应用场景。这两种数据库都各有优势,也各有擅长的领域。两者并不是对立的,我们需要根据使用场景选择合适的数据库类型。大型项目通常会同时需要多种数据库,比如使用MySQL作为主数据库存储用户资料和文章,使用Redis(键值对型数据库)缓存数据,使用MongoDB(文档型数据库)存储实时消息。
大多数情况下,SQL数据库都能满足你的需求。
ORM魔法
在web应用里使用原生SQL语句操作数据库主要存在下面两类问题:
1)
手动编写SQL语句比较乏味,而且视图函数中加入太多SQL语句会降低代码的易读性。另外还会容易出现安全问题,比如SQL注入。
2)
常见的开发模式是在开发时使用简单的SQLite,而在部署时切换到MySQL等更健壮的DBMS。但是对于不同的DBMS,我们需要使用不同的Python接口库,这让DBMS的切换变得不太容易。
尽管使用ORM可以避免SQL注入问题,但仍然需要对传入的查询参数进行验证。另外,在执行原生SQL语句时也要注意避免使用字符串拼接或字符串格式化的方式传入参数。
使用ORM可以很大程度上解决这些问题。它会自动帮你处理查询参数的转义,尽可能的避免SQL注入的发生。另外,它为不同的DBMS提供统一的接口,让切换工作变得简单。ORM扮演翻译的角色,能将我们的python语言转换为DBMS能够读懂的SQL指令,让我们能用python来操控数据库。
ORM把底层的SQL数据实体转化成高层的python对象,这样你可以不用了解SQL,只通过python代码即可完成数据库操作,ORM主要实现了三层映射关系:
1)
表—》python类。
2)
字段(列)--》类属性
3)
记录(行)--》类实例
比如,我们要创建一个contacts表来存储留言,其中包含用户名称和电话号码两个字段。在SQL中,下面的代码用来创建这个表:
CREATE TABLE contacts( name varchar(100) NOT NULL, phone_number varchar(32), );
如果使用ORM,可以使用类似下面的python类来定义这个表:
from foo_orm import Model, Column, String class Contact(Model):
__tablename__ = 'contacts'
name = Column(String(100), nullable=False)
phone_number = Column(String(32))
要向表中插入一条记录,需要使用下面的SQL语句:
INSERT INTO contacts(name, phone_number) VALUES(‘Sam Xia’, ‘1234567889’)
使用ORM则只需要创建一个Contact类的实例,传入对应的参数表示各个列的数据即可。
下面的代码和上面的SQL语句效果相同:
contact = Contact(name = ‘Sam Xia’, phone_number=’123456789’)
除了便于使用,ORM还有下面这些优点:
1) 灵活性好。既可以使用高层对象来操作数据库,又支持执行原生SQL语句
2) 提升效率。从高层对象转换成原生SQL会牺牲一些性能,但这微不足道的性能牺牲换取的是巨大的效率提升。
3) 可移植性好。ORM通常支持多种DBMS,包括MySQL、PostgreSQL、SQLite等。可以随意更换DBMS,只需要稍微改动少量配置。
使用python实现的ORM有SQLAlchemy、Peewee、PonyORM等。其中SQLAlchemy是python社区使用最广泛的ORM。SQL-Alchemy,直译过来就是SQL炼金术。