¶安装
使用 pip install sqlalchemy
进行安装
默认情况下,SqlAlchemy 是直接支持 SQLite3 的,不需要安装额外的驱动程序,但是想要支持其他的数据库需要安装相应的驱动程序
- 若想要支持 mysql,需要安装 pymysql 驱动
¶使用(以使用 MySql 为例)
¶连接数据库
要连接数据库,首先需要建立一个 SqlAlchemy 引擎,为数据库创建一个公共接口来执行 SQL 语句
示例:
1 | from sqlalchemy import create_engine |
注意:
create_engine
函数会返回一个引擎的实例。但是,在调用需要使用连接的操作(比如查询)之前,它实际上并不会打开连接。
¶选择 SQLAlchemy Core 还是 SQLAlchemy ORM
在使用 SQLAlchemy 构建应用程序之前,你要决定主要使用 ORM 还是 Core,这通常取决于多个因素和个人偏好。两者语法略有不同,最大的区别是把数据看作模式还是业务对象。
Core 的视图以模式为中心,与传统的 SQL 一样,关注的是表、键和索引结构。ORM 把元数据和业务对象中的底层模式和结构封装起来,这种封装使得和数据库的交互变得更加容易,就像在使用普通的 Python 代码一样。
¶使用 SQLAlchemy Core 为应用程序提供数据库服务
¶模式和类型
SqlAlchemy 提供了四种类型:
- 通用类型
- SQL类型
- 厂商自定义类型
- 用户定义类型
SQLAlchemy 定义了大量的通用类型,可以从 sqlalchemy.type 模块中导入,为了方便起见,在 sqlalchemy 中也可以直接使用。
通用类型及对应关系表:
SQLAlchemy | Python | SQL |
---|---|---|
BigInteger | int | BIGINT |
Boolean | bool | BOOLEAN或SMALLINT |
Date | datetime.date | DATE(SQLite:STRING) |
DateTime | datetime.datetime | DATETIME(SQLite:STRING) |
Enum | str | ENUM或VARCHAR |
Float | float或Decimal | FLOAT或REAL |
Integer | int | INTEGER |
Interval | datetime.timedelta | INTERVAL或DATE(从纪元开始) |
LargeBinary | byte | BLOB或BYTEA |
Numeric | decimal.Decimal | NUMERIC或DECIMAL |
Unicode | unicode | UNICODE或VARCHAR |
Text | str | CLOB或TEXT |
Tiem | datetime.time | DATETIME |
除了通用类型,还可以使用SQL标准类型和厂商自定义类型,是为了方便在通用类型无法满足时调用的。
区别:
- SQL标准类型在 sqlalchemy.type 中可用,且为了和通用类型区分,都要大写
- 厂商自定义类型只使用于特定的后端数据库类型,可以通过所选方言文档以及 Sqlalchemy 站点确定可用的类型,都在sqlalchemy.dialects 模块中,且每个数据库方言都有若干子模块,如使用 postgresql 数据库中的json格式,可以使用
from sqlalchemy.dialects.postgresql import json
进行调用。
示例:
1 | from sqlalchemy import create_engine |
¶插入数据
使用 insetr()
作为表实例cookies的方法进行数据插入
示例:
1 | # 插入数据 |
在SQL语句中,我们提供的值被替换为
:column_name
的形式,这就是 SQLAlchemy 通过 str() 函数表示参数的方式,参数用来帮助确保数据被正确的转义,从而减少 SQL 注入攻击等安全问题。
ins 对象的 compile() 方法返回一个 SQLCompiler 对象,该对象允许我们通过 params 属性访问隋查询一起发送的实际参数。
insert
除了可以作为表实例cookies的方法来使用外,还可以作为单独的函数来使用,此时表实例cookies作为函数的参数,事实上,这种方法更加贴近于数据库的 insert 写法,因此更加推荐这种用法
示例:
1 | from sqlalchemy import insert |
¶执行插入数据
执行插入语句,执行前需要先添加引擎并连接
示例:
1 | from sqlalchemy import create_engine |
连接对象的execute
方法不仅可以接受语句,还可以在语句之后接受关键字参数值,在编译语句时,它会向列列表添加每个关键字参数键,并将它们的每个值添加到 SQL 语句的 VALUES 部分,但是这种用法不常用,但是他很好的说明了语句在发送到数据库服务器之前是如何编译和组装的。可以通过使用一个字典列表一次插入很多条记录,字典里面包含我们要提交的数据。
示例:
1 | ins = cookies.insert() # 此处的ins也可以这么写 ins = insert(cookies) |
进阶版,通过使用一个字典列表一次插入很多条记录,字典里面包含我们要提交的数据
示例:
1 | inventory_list = [ |
注意:创建字典列表的时候,字典必须拥有完全相同的键,sqlalchemy 会根据列表中的第一个字典编译语句,如果后续的字典不同会出错
¶查找数据
使用 select()
作为表实例 cookies 的方法进行数据查询,select() 方法需要一个列的列表来选择,为了方便,还可以直接接受一个 Table 实例(这里的cookies表),则此时选中该表中的所有的内容。
示例:
1 | from sqlalchemy import select |
与 insert()
方法的调用方式一致,Table 实例对象也提供了 select() 方法
示例:
1 | s = cookies.select() # 直接使用cookies的select方法 |
¶ResultProxy
ResultProxy 是 DBAPI 游标对象的包装器,主要目的是让语句返回的结果更容易使用和操作,比如,ResultProxy 允许通过索引,名称或 column对象 进行访问,从而简化了对查询结果的处理。
通过上面的例子中可以发现,调用 connection.execute(s)
执行数据库操作之后返回的实例对象就是 ResultProxy 类型。
示例:
1 | first_row = result[0] # 获取ResultProxy的第一行 |
注意:例子中的result是调用 ResultProxy 提供的 fetchall() 方法得到的
通过迭代的方式获得 ResultProxy 的项
示例:
1 | s = select([cookies]) |
ResultProxy 提供的可调用方法和属性包括:
- 使用
rowcount
属性获得本次操作的数据条数,如在查询操作中,就是获得本次查询获得数据条数 - 使用
inserted_primary_key
属性获得数据插入操作中,插入的数据的主键值 first()
方法,若有记录,则返回第一个记录并关闭连接,此时想再操作会报异常sqlalchemy.exc.ResourceClosedError: This result object is closed.
fetchone()
方法,返回一条数据,并保持光标为打开的状态,以便你做更多的获取调用,光标会记录位置。fetchall()
方法,以列表的形式返回所有的记录,光标会记录位置。- 使用 for 循环得到数据,光标会记录位置。
scalar()
方法,只返回查询到的第一个数据的最左边的列,且只返回第一行的数据。- 如果想产看结果集中的多个列,可以使用
keys()
方法来获得列名列表。
具体方法的调用,可以遵循以下方针:
- 获取单条记录时,要多用 first() 方法,尽量不要使用 fetchone() 和 scalar() 方法,因为对程序员来说,first() 方法更加清晰,需要注意,使用 first() 返回第一条记录后将自动关闭连接。
- 尽量使用可迭代对象 ResultProxy,而不要用 fetchall() 和 fetchone() 方法,因为前者的内存效率更高,而且我们往往一次只对一条记录进行操作。
- 避免使用 fetchone() 方法,因为如果不小心,他会一直让连接处在打开状态,因为使用 fetchone() 会返回一行,并保持光标为打开的状态,以便你做更多的获取调用。
- 谨慎使用 scalar() 方法,因为如果查询返回多行多列,就会引发错误,多行多列在测试过程中会经常丢失。
以上的查询方式得到各条记录的所有列,通常我们只需要使用这些列中的一部分。如果额外的列中的数据量很大,就会到导致应用程序运行变慢,消耗的内存远超预期,下面我们可以对查询返回的列数进行限制。
示例:
1 | # 为了限制返回的列数,需要以列表的形式将查询的列传递给 select() 方法 |
[*] cookies.c.cookie_name
表示cookies表的Column对象(列对象)cookie_name
¶排序
需要将查找返回的数据按照特性的顺序排列,可以使用 order_by()
语句,默认是升序的方式排序。
示例:
1 | s = select([cookies.c.cookie_name, cookies.c.quantity]).order_by(cookies.c.quantity) |
想要降序或者倒序排列,可以使用desc()
语句,但是,这需要额外的导包 from sqlalchemy import desc
示例:
1 | from sqlalchemy import desc |
除了调用 desc()
函数,desc()
还可以作为Column对象的方法进行调用,像这样cookies.c.quantity.desc()
,但是如果在长语句中这样使用,可能会造成阅读困难,所以建议将desc()
作为函数进行使用。
提示:所有的操作语句在都可以通过直接打印的方式查看到对应的 SQL 语句。
¶对返回数据的结果条数进行限制
虽然可以对 ResultProxy 使用 first()
和 fetchone()
获得一行数据,但是实际查询了所有的记录,使用 limit()
方法让 limit语句 成为查询的一部分,对查询的记录的条数进行限制。
示例:
1 | # 查询两种库存量最少的cookies |
注意最后一行,想再次从 ResultProxy 对象中获得数据的时候,发现获取不到了,这是因为 ResultProxy 对象是DBAPI游标对象的包装器,游标实际上是一种能从包括多条数据记录的结果集中每次提取一条记录的机制。游标充当指针的作用,ResultProxy 对象在被使用一次后,会记录位置。
¶内置SQL函数和标签
许多数据库设计都包含了 SQL 函数,这些函数的设计目的是为了让某些操作可以直接在数据库服务器上使用,如 SUM 函数,SqlAlchemy可以利用后端数据库中的 SQL 函数,常见的函数为 SUM 以及 COUNT,想使用这两个函数,需要导入 sqlalchemy.sql.func
模块,这些函数包装在他们要操作的列上。
示例:
1 | # 计算cookie的总数 |
建议使用导入 func 模块的方式,因为直接导入 sum 可以会引起问题,而且还容易和Python内置的sum函数混淆
count 计数
示例:
1 | s = select([func.count(cookies.c.cookie_name)]) |
RequestProxy 和 RowProxy 类型都可以调用keys()方法,用来返回列名
print(recode.count_1)是以列名的形式打印内容的,因为使用使用COUNT和SUM方法都会以<func_name>_<position>
的形式起别名,position是调用COUNT和SUM方法的次数,如第二个调用的count()函数得到的别名将是count_2。但是这样的命名方式实在不够明确,SQLAlchemy提供了 label()
函数来解决这个问题,可以使用 label()
函数来为列取个别名。
示例:
1 | s = select([func.count(cookies.c.cookie_name).label('inventory_count')]) |
只需要在更改的列对象上调用 label()
函数即可,这意味着,我们在查找原有列名时,也可以对返回的列取别名,像这样 s = select([cookies.c.cookie_name.label('cookie_name_count')])
。
除了 fitchall 得到的是 list 类型,其他的方法得到的行数据都是 RowProxy 类型的单个数据,当然 fitchall()得到的是行数据的集合,所以列表中的每一项都是 RowProxy 类型的数据。
¶过滤
对查询进行过滤是通过 where()
语句来完成,可以把多个 where()子句 接在一起使用,功能就像传统的 SQL 语句中的 AND 一样。
示例:
1 | s = select([cookies]).where(cookies.c.cookie_name == "chocolate chip") |
注意:RowProxy 对象的 items() 方法返回得到由列名和值组成的元组列表,注意是记录对象 RowProxy 含有该方法,而不是 RequestProxy。
使用 like
方法进行模糊查询
示例:查询包含 chocolate 的 cookie 名
1 | s = select([cookies]).where(cookies.c.cookie_name.like('%chocolate%')) |
¶ClauseElement
ClauseElement
是在子句中使用的实体,一般是表中的列。不过,与列不同的是ClauseElement拥有许多额外的功能,如之前我们调用的like()方法,此外还有许多额外的方法。
方法 | 用途 |
---|---|
between(cleft, cright) | 查找cleft和cright之间的内容 |
concat(column_two) | 连接列 |
destinct() | 查找列的唯一值 |
in_([list]) | 查找列在列表中的位置 |
is_(None) | 查找列None的位置(通常用于检查Null和None) |
contains(string) | 查找包含string的列(区分大小写) |
endswith(string) | 查找以string结尾的列(区分大小写) |
like(string) | 查找与string匹配的列(区分大小写) |
startwith(string) | 查找以string开头的列(区分大小写) |
ilike(string) | 查找与string匹配的列(不区分大小写) |
以上这些方法都有相反的方法,例如 notlike() 和 notin_()。not<方法> 这种命名方式约定的唯一例外是不带下划线的 isnot() 方法。
¶运算符
除了 ClauseElement 中的这些方法之外,还可以在 where() 语句中使用运算符,SQLAlchemy 针对大多数的Python运算符都做了重载,包括标准的比较运算符(==、!=、<、>、<=、=>)
,他们的功能和在Python中一样。在与None比较时,==被重载为IS NULL语句。算数运算符(+、-、*、/和%)
还可以用来独立于数据库的字符串做连接处理。
使用+连接字符串
示例:
1 | s = select([cookies.c.cookie_name, 'SKU-' + cookies.c.cookie_sku]) |
运算符的另外一种用法是根据多个列来计算值
示例:
1 | # 计算各种cookie的库存价值 |
cast() 是另一个允许做类型转换的函数,使用方式是 cast(数据,转换类型),这里如果不使用 cast,也是可以获得相应的数据的,此时我们可以在 python 语句中对数据类型进行强制转换:
1 | print("{} - {:.2f}".format(row.cookie_name, row.inv_cost)) |
区别是前者直接在查询返回结果时就进行了格式化,而后者需要打印时手动完成转换。
¶布尔运算符
SQLAlchemy 还支持布尔运算符 AND、OR 和 NOT,他们用位运算符(&、|和~)来表示。受Python运算符优先级规则的影响,应当尽量使用下面的连接词,而不要使用这些重载的运算符。
¶连接词
and_()
、or_()
、not_()
。为了实现某种期望的效果,我们既可以把多个where()子句连接在一起,也可以使用连接词来实现,而且使用连接词的可读性更好,功能性更强。
示例:
1 | from sqlalchemy import and_,or_,not_ |
¶复杂过滤
连接词 配合 ClauseElement 使用,实现复杂查询
示例:
1 | s = select([cookies]).where( |
未完待续 …