Sqlalchemy 简明教程

SQLAlchemy - Quick Guide

SQLAlchemy - Introduction

SQLAlchemy 是一个流行的 SQL 工具包和 Object Relational Mapper 。它用 Python 编写,为应用程序开发人员提供了 SQL 的全部功能和灵活性。它是一个 open sourcecross-platform software ,在 MIT 许可证下发布。

SQLAlchemy is a popular SQL toolkit and Object Relational Mapper. It is written in Python and gives full power and flexibility of SQL to an application developer. It is an open source and cross-platform software released under MIT license.

SQLAlchemy 以其对象关系映射器 (ORM) 而闻名,可以使用它,将类映射到数据库,从而允许对象模型和数据库模式从一开始就以一种干净解耦的方式开发。

SQLAlchemy is famous for its object-relational mapper (ORM), using which, classes can be mapped to the database, thereby allowing the object model and database schema to develop in a cleanly decoupled way from the beginning.

随着 SQL 数据库的规模和性能开始变得重要,它们的行为不像对象集合。另一方面,随着对象集合中的抽象开始变得重要,它们的行为不像表和行。SQLAlchemy 旨在适应这两个原则。

As size and performance of SQL databases start to matter, they behave less like object collections. On the other hand, as abstraction in object collections starts to matter, they behave less like tables and rows. SQLAlchemy aims to accommodate both of these principles.

出于这个原因,它采用了 data mapper pattern (like Hibernate) rather than the active record pattern used by a number of other ORMs 。使用 SQLAlchemy,将从不同的角度看待数据库和 SQL。

For this reason, it has adopted the data mapper pattern (like Hibernate) rather than the active record pattern used by a number of other ORMs. Databases and SQL will be viewed in a different perspective using SQLAlchemy.

Michael Bayer 是 SQLAlchemy 的原始作者。它的初始版本发布于 2006 年 2 月。最新版本编号为 1.2.7,最近于 2018 年 4 月发布。

Michael Bayer is the original author of SQLAlchemy. Its initial version was released in February 2006. Latest version is numbered as 1.2.7, released as recently as in April 2018.

What is ORM?

ORM(对象关系映射)是一种编程技术,用于在面向对象编程语言的不兼容类型系统之间转换数据。通常,在面向对象 (OO) 语言(如 Python)中使用的类型系统包含非标量类型。这些不能表示为整数和字符串之类的基元类型。因此,OO 编程器必须将标量数据中的对象转换为与后端数据库交互。然而,在大多数数据库产品(如 Oracle、MySQL 等)中的数据类型是主要的。

ORM (Object Relational Mapping) is a programming technique for converting data between incompatible type systems in object-oriented programming languages. Usually, the type system used in an Object Oriented (OO) language like Python contains non-scalar types. These cannot be expressed as primitive types such as integers and strings. Hence, the OO programmer has to convert objects in scalar data to interact with backend database. However, data types in most of the database products such as Oracle, MySQL, etc., are primary.

在 ORM 系统中,每个类都映射到底层数据库中的一个表。ORM 负责处理这些问题,让你专注于对系统逻辑进行编程,而不再需要自己编写乏味的数据库接口代码。

In an ORM system, each class maps to a table in the underlying database. Instead of writing tedious database interfacing code yourself, an ORM takes care of these issues for you while you can focus on programming the logics of the system.

SQLAlchemy - Environment setup

让我们讨论使用 SQLAlchemy 所需的环境设置。

Let us discuss the environmental setup required to use SQLAlchemy.

必须使用高于 2.7 的任何 Python 版本才能安装 SQLAlchemy。最简单的安装方法是使用 Python 包管理器 pip 。此实用程序与 Python 的标准发行版捆绑在一起。

Any version of Python higher than 2.7 is necessary to install SQLAlchemy. The easiest way to install is by using Python Package Manager, pip. This utility is bundled with standard distribution of Python.

pip install sqlalchemy

使用上述命令,我们可以从 python.org 下载 SQLAlchemy 的 latest released version ,并将其安装到系统中。

Using the above command, we can download the latest released version of SQLAlchemy from python.org and install it to your system.

对于 Python 的 conda 发行版,可以使用 conda terminal 通过以下命令安装 SQLAlchemy:

In case of anaconda distribution of Python, SQLAlchemy can be installed from conda terminal using the below command −

conda install -c anaconda sqlalchemy

也可以从下列源代码安装 SQLAlchemy:

It is also possible to install SQLAlchemy from below source code −

python setup.py install

SQLAlchemy 被设计为使用专为某个特定数据库而构建的 DBAPI 实现来运行。它使用方言系统与各种类型的 DBAPI 实现和数据库通信。所有方言都要求已安装相应的 DBAPI 驱动程序。

SQLAlchemy is designed to operate with a DBAPI implementation built for a particular database. It uses dialect system to communicate with various types of DBAPI implementations and databases. All dialects require that an appropriate DBAPI driver is installed.

以下是包含的方言:

The following are the dialects included −

  1. Firebird

  2. Microsoft SQL Server

  3. MySQL

  4. Oracle

  5. PostgreSQL

  6. SQLite

  7. Sybase

要检查 SQLAlchemy 是否正确安装以及了解其版本,请在 Python 提示符中输入以下命令:

To check if SQLAlchemy is properly installed and to know its version, enter the following command in the Python prompt −

>>> import sqlalchemy
>>>sqlalchemy.__version__
'1.2.7'

SQLAlchemy Core – Expression Language

SQLAlchemy 核心包括 SQL rendering engine, DBAPI integration, transaction integrationschema description services 。SQLAlchemy 核心使用 SQL 表达式语言,提供 schema-centric usage 范式,而 SQLAlchemy ORM 则是 domain-centric mode of usage

SQLAlchemy core includes SQL rendering engine, DBAPI integration, transaction integration, and schema description services. SQLAlchemy core uses SQL Expression Language that provides a schema-centric usage paradigm whereas SQLAlchemy ORM is a domain-centric mode of usage.

SQL 表达式语言呈现了一个使用 Python 构造来表示关系数据库结构和表达式的系统。它呈现了一个系统来直接表示关系数据库的原始构造,这不同于 ORM,ORM 呈现了一个高级的、抽象化的用法模式,它本身就是对表达式语言应用用法的示例。

The SQL Expression Language presents a system of representing relational database structures and expressions using Python constructs. It presents a system of representing the primitive constructs of the relational database directly without opinion, which is in contrast to ORM that presents a high level and abstracted pattern of usage, which itself is an example of applied usage of the Expression Language.

表达式语言是 SQLAlchemy 的核心组件之一。它允许程序员在 Python 代码中指定 SQL 语句,并直接将其用于更加复杂的查询。表达式语言不依赖于后端,并且全面涵盖了原始 SQL 的各个方面。它比 SQLAlchemy 中的任何其他组件都更接近原始 SQL。

Expression Language is one of the core components of SQLAlchemy. It allows the programmer to specify SQL statements in Python code and use it directly in more complex queries. Expression language is independent of backend and comprehensively covers every aspect of raw SQL. It is closer to raw SQL than any other component in SQLAlchemy.

表达式语言直接表示关系数据库的原始构造。由于 ORM 基于表达式语言之上,因此典型的 Python 数据库应用程序可能重叠使用两者的用法。该应用程序可以单独使用表达式语言,尽管它必须定义自己的系统,将应用程序概念转换为各个数据库查询。

Expression Language represents the primitive constructs of the relational database directly. Because the ORM is based on top of Expression language, a typical Python database application may have overlapped use of both. The application may use expression language alone, though it has to define its own system of translating application concepts into individual database queries.

表达式语言的语句将由 SQLAlchemy 引擎转换为相应的原始 SQL 查询。现在,我们将学习如何创建引擎并借助于它执行各种 SQL 查询。

Statements of Expression language will be translated into corresponding raw SQL queries by SQLAlchemy engine. We shall now learn how to create the engine and execute various SQL queries with its help.

SQLAlchemy Core - Connecting to Database

在上一章中,我们讨论了 SQLAlchemy 中的表达式语言。现在,让我们继续执行连接到数据库的步骤。

In the previous chapter, we have discussed about expression Language in SQLAlchemy. Now let us proceed towards the steps involved in connecting to a database.

引擎类连接一个 Pool and Dialect together 来提供数据库 connectivity and behavior 的源。使用 create_engine() 函数对引擎类的一个对象进行实例化。

Engine class connects a Pool and Dialect together to provide a source of database connectivity and behavior. An object of Engine class is instantiated using the create_engine() function.

create_engine() 函数将数据库作为单个参数。数据库不需要在任何地方进行定义。标准调用形式必须将 URL 作为第一个位置参数进行发送,它通常是一个表示数据库方言和连接参数的字符串。使用下面提供的代码,我们可以创建一个数据库。

The create_engine() function takes the database as one argument. The database is not needed to be defined anywhere. The standard calling form has to send the URL as the first positional argument, usually a string that indicates database dialect and connection arguments. Using the code given below, we can create a database.

>>> from sqlalchemy import create_engine
>>> engine = create_engine('sqlite:///college.db', echo = True)

对于 MySQL database ,使用以下命令−

For a MySQL database, use the below command −

engine = create_engine("mysql://user:pwd@localhost/college",echo = True)

为了具体指定要用于连接的 DB-APIURL string 需要采用以下形式−

To specifically mention DB-API to be used for connection, the URL string takes the form as follows −

dialect[+driver]://user:password@host/dbname

例如,如果你正在使用 PyMySQL driver with MySQL ,使用以下命令 −

For example, if you are using PyMySQL driver with MySQL, use the following command −

mysql+pymysql://<username>:<password>@<host>/<dbname>

echo flag 是设置 SQLAlchemy 日志记录的一个快捷方式,它是通过 Python 的标准日志记录模块来完成的。在后续章节中,我们将学习所有生成的 SQL。若要隐藏冗余输出,请将 echo 属性设置为 None 。create_engine() 函数的其他参数可能是方言专有的。

The echo flag is a shortcut to set up SQLAlchemy logging, which is accomplished via Python’s standard logging module. In the subsequent chapters, we will learn all the generated SQLs. To hide the verbose output, set echo attribute to None. Other arguments to create_engine() function may be dialect specific.

create_engine() 函数返回一个 Engine object 。引擎类的某些重要方法是 −

The create_engine() function returns an Engine object. Some important methods of Engine class are −

Sr.No.

Method & Description

1

connect() Returns connection object

2

execute() Executes a SQL statement construct

3

begin() Returns a context manager delivering a Connection with a Transaction established. Upon successful operation, the Transaction is committed, else it is rolled back

4

dispose() Disposes of the connection pool used by the Engine

5

driver() Driver name of the Dialect in use by the Engine

6

table_names() Returns a list of all table names available in the database

7

transaction() Executes the given function within a transaction boundary

SQLAlchemy Core - Creating Table

现在,让我们讨论如何使用创建表函数。

Let us now discuss how to use the create table function.

SQL 表达式语言针对表列构建其表达式。SQLAlchemy Column 对象表示数据库表中的 column ,数据库表反过来表示为 Tableobject 。Metadata 包含表定义和关联的对象,例如索引、视图、触发器等。

The SQL Expression Language constructs its expressions against table columns. SQLAlchemy Column object represents a column in a database table which is in turn represented by a Tableobject. Metadata contains definitions of tables and associated objects such as index, view, triggers, etc.

因此,SQLAlchemy Metadata 中 MetaData 类的对象是 Table 对象及它们关联的架构构造的集合。它包含一组 Table 对象,以及与引擎或连接可选绑定。

Hence an object of MetaData class from SQLAlchemy Metadata is a collection of Table objects and their associated schema constructs. It holds a collection of Table objects as well as an optional binding to an Engine or Connection.

from sqlalchemy import MetaData
meta = MetaData()

MetaData 类的构造函数可以具有默认情况下为 None 的 bind 和 schema 参数。

Constructor of MetaData class can have bind and schema parameters which are by default None.

接下来,我们在上述元数据目录中使用 the Table construct 定义所有表,该对象类似于常规的 SQL CREATE TABLE 语句。

Next, we define our tables all within above metadata catalog, using the Table construct, which resembles regular SQL CREATE TABLE statement.

Table 类的对象表示数据库中某个对应的表。它的构造函数接受以下参数:

An object of Table class represents corresponding table in a database. The constructor takes the following parameters −

Name

Name of the table

Metadata

MetaData object that will hold this table

Column(s)

One or more objects of column class

Column 对象表示 database table 中的 column 。构造函数接受名称、类型和其他参数,例如 primary_key、autoincrement 及其他约束。

Column object represents a column in a database table. Constructor takes name, type and other parameters such as primary_key, autoincrement and other constraints.

SQLAlchemy 将 Python 数据匹配到其中定义的最佳通用列数据类型。其中一些通用数据类型如下:

SQLAlchemy matches Python data to the best possible generic column data types defined in it. Some of the generic data types are −

  1. BigInteger

  2. Boolean

  3. Date

  4. DateTime

  5. Float

  6. Integer

  7. Numeric

  8. SmallInteger

  9. String

  10. Text

  11. Time

要在 college 数据库中创建一个 students table ,请使用以下代码段:

To create a students table in college database, use the following snippet −

from sqlalchemy import Table, Column, Integer, String, MetaData
meta = MetaData()

students = Table(
   'students', meta,
   Column('id', Integer, primary_key = True),
   Column('name', String),
   Column('lastname', String),
)

create_all() 函数使用此引擎对象创建所有定义的表对象,并将信息存储在元数据中。

The create_all() function uses the engine object to create all the defined table objects and stores the information in metadata.

meta.create_all(engine)

下面给出了完成的代码,它将在其中创建一张 students 表,生成 SQLite 数据库 college.db。

Complete code is given below which will create a SQLite database college.db with a students table in it.

from sqlalchemy import create_engine, MetaData, Table, Column, Integer, String
engine = create_engine('sqlite:///college.db', echo = True)
meta = MetaData()

students = Table(
   'students', meta,
   Column('id', Integer, primary_key = True),
   Column('name', String),
   Column('lastname', String),
)
meta.create_all(engine)

由于 create_engine() 函数的 echo 属性设置为 True ,因此控制台将显示表的实际 SQL 查询,如下所示:

Because echo attribute of create_engine() function is set to True, the console will display the actual SQL query for table creation as follows −

CREATE TABLE students (
   id INTEGER NOT NULL,
   name VARCHAR,
   lastname VARCHAR,
   PRIMARY KEY (id)
)

college.db 将在当前工作目录中创建。要检查 students 表是否创建,你可以使用任何 SQLite GUI 工具(如 SQLiteStudio )来打开该数据库。

The college.db will be created in current working directory. To check if the students table is created, you can open the database using any SQLite GUI tool such as SQLiteStudio.

下图显示了在数据库中创建的 students 表:

The below image shows the students table that is created in the database −

students table

SQLAlchemy Core - SQL Expressions

在本章中,我们将简要关注 SQL 表达式及其函数。

In this chapter, we will briefly focus on the SQL Expressions and their functions.

SQL 表达式是使用相对于目标表对象的对应方法构建的。例如,可以通过执行 insert() 方法来创建 INSERT 语句,如下所示:

SQL expressions are constructed using corresponding methods relative to target table object. For example, the INSERT statement is created by executing insert() method as follows −

ins = students.insert()

上述方法的结果是一个可以利用 str() 函数验证的插入对象。以下代码插入详细信息,如学生 id、名字、姓氏。

The result of above method is an insert object that can be verified by using str() function. The below code inserts details like student id, name, lastname.

'INSERT INTO students (id, name, lastname) VALUES (:id, :name, :lastname)'

可以按 values() 方法在特定字段中插入值以插入对象。其代码如下所示:

It is possible to insert value in a specific field by values() method to insert object. The code for the same is given below −

>>> ins = users.insert().values(name = 'Karan')
>>> str(ins)
'INSERT INTO users (name) VALUES (:name)'

Python 控制台上回显的 SQL 不会显示实际值(本例中为“Karan”)。SQLAlchemy 会生成一个绑定参数,它在语句的已编译表单中可见。

The SQL echoed on Python console doesn’t show the actual value (‘Karan’ in this case). Instead, SQLALchemy generates a bind parameter which is visible in compiled form of the statement.

ins.compile().params
{'name': 'Karan'}

同样, update(), delete()select() 等方法分别创建 UPDATE、DELETE 和 SELECT 表达式。后面的章节中将会学习这些表达式的相关内容。

Similarly, methods like update(), delete() and select() create UPDATE, DELETE and SELECT expressions respectively. We shall learn about them in later chapters.

SQLAlchemy Core - Executing Expression

在上一章中,我们了解了 SQL 表达式。本章中,我们将会了解如何执行这些表达式。

In the previous chapter, we have learnt SQL Expressions. In this chapter, we shall look into the execution of these expressions.

要执行所得的 SQL 表达式,必须首先 obtain a connection object representing an actively checked out DBAPI connection resourcefeed the expression object ,如下面的代码所示。

In order to execute the resulting SQL expressions, we have to obtain a connection object representing an actively checked out DBAPI connection resource and then feed the expression object as shown in the code below.

conn = engine.connect()

以下 insert() 对象可用于 execute() 方法:

The following insert() object can be used for execute() method −

ins = students.insert().values(name = 'Ravi', lastname = 'Kapoor')
result = conn.execute(ins)

控制台将显示 SQL 表达式执行结果,如下面所示:

The console shows the result of execution of SQL expression as below −

INSERT INTO students (name, lastname) VALUES (?, ?)
('Ravi', 'Kapoor')
COMMIT

以下是显示使用 SQLAlchemy 核心技术执行 INSERT 查询的完整代码段:

Following is the entire snippet that shows the execution of INSERT query using SQLAlchemy’s core technique −

from sqlalchemy import create_engine, MetaData, Table, Column, Integer, String
engine = create_engine('sqlite:///college.db', echo = True)
meta = MetaData()

students = Table(
   'students', meta,
   Column('id', Integer, primary_key = True),
   Column('name', String),
   Column('lastname', String),
)

ins = students.insert()
ins = students.insert().values(name = 'Ravi', lastname = 'Kapoor')
conn = engine.connect()
result = conn.execute(ins)

可以使用 SQLite Studio 打开数据库并查看结果,如下图所示:

The result can be verified by opening the database using SQLite Studio as shown in the below screenshot −

sqlite studio

结果变量被称为 ResultProxy object 。这类似于 DBAPI 游标对象。我们可以使用 ResultProxy.inserted_primary_key 获取语句中生成的主键值的信息,如下所示:

The result variable is known as a ResultProxy object. It is analogous to the DBAPI cursor object. We can acquire information about the primary key values which were generated from our statement using ResultProxy.inserted_primary_key as shown below −

result.inserted_primary_key
[1]

要使用 DBAPI 的 execute many() 方法发出多个插入操作,可以发送一个列表,其中每个列表包含一组要插入的不同参数。

To issue many inserts using DBAPI’s execute many() method, we can send in a list of dictionaries each containing a distinct set of parameters to be inserted.

conn.execute(students.insert(), [
   {'name':'Rajiv', 'lastname' : 'Khanna'},
   {'name':'Komal','lastname' : 'Bhandari'},
   {'name':'Abdul','lastname' : 'Sattar'},
   {'name':'Priya','lastname' : 'Rajhans'},
])

此操作反映在表的数据视图中,如图所示:

This is reflected in the data view of the table as shown in the following figure −

table data view

SQLAlchemy Core - Selecting Rows

在本章中,我们将讨论表对象中选择行的概念。

In this chapter, we will discuss about the concept of selecting rows in the table object.

表对象的 select() 方法使我们能够 construct SELECT expression

The select() method of table object enables us to construct SELECT expression.

s = students.select()

select 对象转换成 SELECT query by str(s) function ,如下所示:

The select object translates to SELECT query by str(s) function as shown below −

'SELECT students.id, students.name, students.lastname FROM students'

我们可以使用此 select 对象作为连接对象的 execute() 方法的参数,如下面的代码所示:

We can use this select object as a parameter to execute() method of connection object as shown in the code below −

result = conn.execute(s)

当执行上述语句时,Python 外壳将显示以下等效 SQL 表达式:

When the above statement is executed, Python shell echoes following equivalent SQL expression −

SELECT students.id, students.name, students.lastname
FROM students

结果变量相当于 DBAPI 中光标。我们现在可以使用 fetchone() method 提取记录。

The resultant variable is an equivalent of cursor in DBAPI. We can now fetch records using fetchone() method.

row = result.fetchone()

在下表中,所有选定的行都可以通过 for loop 打印出来,如下所示:

All selected rows in the table can be printed by a for loop as given below −

for row in result:
   print (row)

从学生表中打印所有行的完整代码如下所示:

The complete code to print all rows from students table is shown below −

from sqlalchemy import create_engine, MetaData, Table, Column, Integer, String
engine = create_engine('sqlite:///college.db', echo = True)
meta = MetaData()

students = Table(
   'students', meta,
   Column('id', Integer, primary_key = True),
   Column('name', String),
   Column('lastname', String),
)

s = students.select()
conn = engine.connect()
result = conn.execute(s)

for row in result:
   print (row)

在 Python shell 中显示的输出如下:

The output shown in Python shell is as follows −

(1, 'Ravi', 'Kapoor')
(2, 'Rajiv', 'Khanna')
(3, 'Komal', 'Bhandari')
(4, 'Abdul', 'Sattar')
(5, 'Priya', 'Rajhans')

SELECT 查询的 WHERE 子句可通过使用 Select.where() 应用。例如,如果要显示 id >2 的行

The WHERE clause of SELECT query can be applied by using Select.where(). For example, if we want to display rows with id >2

s = students.select().where(students.c.id>2)
result = conn.execute(s)

for row in result:
   print (row)

这里 c attribute is an alias for column 。将在 shell 上显示以下输出:

Here c attribute is an alias for column. Following output will be displayed on the shell −

(3, 'Komal', 'Bhandari')
(4, 'Abdul', 'Sattar')
(5, 'Priya', 'Rajhans')

这里,我们需要注意,select 对象也可以在 sqlalchemy.sql 模块中通过 select() 函数获取。select() 函数需要 table 对象作为参数。

Here, we have to note that select object can also be obtained by select() function in sqlalchemy.sql module. The select() function requires the table object as argument.

from sqlalchemy.sql import select
s = select([users])
result = conn.execute(s)

SQLAlchemy Core - Using Textual SQL

对于已知 SQL 的情况且无需语句支持动态特征的情况,SQLAlchemy 允许你仅使用字符串。text() 构造被用于编写传递到数据库的大致没有修改的文本语句。

SQLAlchemy lets you just use strings, for those cases when the SQL is already known and there isn’t a strong need for the statement to support dynamic features. The text() construct is used to compose a textual statement that is passed to the database mostly unchanged.

它构造一个新的 TextClause ,表示直接作为文本 SQL 字符串,如下面的代码所示:

It constructs a new TextClause, representing a textual SQL string directly as shown in the below code −

from sqlalchemy import text
t = text("SELECT * FROM students")
result = connection.execute(t)

text() 相较于简单字符串提供的优势包括:

The advantages text() provides over a plain string are −

  1. backend-neutral support for bind parameters

  2. per-statement execution options

  3. result-column typing behaviour

text() 函数需要名结肠号格式的绑定参数。它们与数据库后端无关。要向参数发送值,我们以附加参数的形式将它们传递到 execute() 方法中。

The text()function requires Bound parameters in the named colon format. They are consistent regardless of database backend. To send values in for the parameters, we pass them into the execute() method as additional arguments.

以下示例在文本 SQL 中使用了绑定参数:

The following example uses bound parameters in textual SQL −

from sqlalchemy.sql import text
s = text("select students.name, students.lastname from students where students.name between :x and :y")
conn.execute(s, x = 'A', y = 'L').fetchall()

text() 函数构造 SQL 表达式,如下所示:

The text() function constructs SQL expression as follows −

select students.name, students.lastname from students where students.name between ? and ?

x = ’A’ 和 y = ’L’ 的值作为参数传递。结果是名字在“A”和“L”之间的行列表

The values of x = ’A’ and y = ’L’ are passed as parameters. Result is a list of rows with names between ‘A’ and ‘L’ −

[('Komal', 'Bhandari'), ('Abdul', 'Sattar')]

text() 构造使用 TextClause.bindparams() 方法支持预先建立的边界值。也可以按以下方式明确设置参数的类型 -

The text() construct supports pre-established bound values using the TextClause.bindparams() method. The parameters can also be explicitly typed as follows −

stmt = text("SELECT * FROM students WHERE students.name BETWEEN :x AND :y")

stmt = stmt.bindparams(
   bindparam("x", type_= String),
   bindparam("y", type_= String)
)

result = conn.execute(stmt, {"x": "A", "y": "L"})

The text() function also be produces fragments of SQL within a select() object that
accepts text() objects as an arguments. The “geometry” of the statement is provided by
select() construct , and the textual content by text() construct. We can build a statement
without the need to refer to any pre-established Table metadata.

from sqlalchemy.sql import select
s = select([text("students.name, students.lastname from students")]).where(text("students.name between :x and :y"))
conn.execute(s, x = 'A', y = 'L').fetchall()

您还可以使用 and_() 函数组合在 WHERE 子句中创建的多个条件,该子句是在 text() 函数的帮助下创建的。

You can also use and_() function to combine multiple conditions in WHERE clause created with the help of text() function.

from sqlalchemy import and_
from sqlalchemy.sql import select
s = select([text("* from students")]) \
.where(
   and_(
      text("students.name between :x and :y"),
      text("students.id>2")
   )
)
conn.execute(s, x = 'A', y = 'L').fetchall()

上面的代码提取了 id 大于 2 的姓名介于“A”和“L”之间的行。代码的输出如下 -

Above code fetches rows with names between “A” and “L” with id greater than 2. The output of the code is given below −

[(3, 'Komal', 'Bhandari'), (4, 'Abdul', 'Sattar')]

SQLAlchemy Core - Using Aliases

SQL 中的别名对应于表的“重命名”版本或 SELECT 语句,该语句在您输入“SELECT * FROM table1 AS a”时随时发生。AS 为表创建了一个新名称。别名允许通过唯一名称引用任何表或子查询。

The alias in SQL corresponds to a “renamed” version of a table or SELECT statement, which occurs anytime you say “SELECT * FROM table1 AS a”. The AS creates a new name for the table. Aliases allow any table or subquery to be referenced by a unique name.

在表的场合,这允许在“FROM”子句中多次命名相同的表。它为该语句表示的列提供了一个父名称,允许相对于该名称对列进行引用。

In case of a table, this allows the same table to be named in the FROM clause multiple times. It provides a parent name for the columns represented by the statement, allowing them to be referenced relative to this name.

在 SQLAlchemy 中,可以使用 From Clause.alias() 方法将任何 Table 型、select() 型或其他可选择对象转换为别名,该方法生成 Alias 型。sqlalchemy.sql 模块中的 alias() 函数表示别名,该别名通常使用 AS 关键字应用于 SQL 语句中的任何表或子查询。

In SQLAlchemy, any Table, select() construct, or other selectable object can be turned into an alias using the From Clause.alias() method, which produces an Alias construct. The alias() function in sqlalchemy.sql module represents an alias, as typically applied to any table or sub-select within a SQL statement using the AS keyword.

from sqlalchemy.sql import alias
st = students.alias("a")

现在可以在 select() 型中使用此别名来引用 students 表 -

This alias can now be used in select() construct to refer to students table −

s = select([st]).where(st.c.id>2)

这转换成的 SQL 表达式如下 -

This translates to SQL expression as follows −

SELECT a.id, a.name, a.lastname FROM students AS a WHERE a.id > 2

现在我们可以使用连接对象的 execute() 方法来执行此 SQL 查询。完整的代码如下 -

We can now execute this SQL query with the execute() method of connection object. The complete code is as follows −

from sqlalchemy.sql import alias, select
st = students.alias("a")
s = select([st]).where(st.c.id > 2)
conn.execute(s).fetchall()

在执行上述代码行时,它会生成以下输出 -

When above line of code is executed, it generates the following output −

[(3, 'Komal', 'Bhandari'), (4, 'Abdul', 'Sattar'), (5, 'Priya', 'Rajhans')]

Using UPDATE Expression

目标表对象上的 update() 方法构建了等效的 UPDATE SQL 表达式。

The update() method on target table object constructs equivalent UPDATE SQL expression.

table.update().where(conditions).values(SET expressions)

结果更新对象上的 values() 方法用于指定 UPDATE 的 SET 条件。如果保留为 None,则 SET 条件将根据在语句执行和/或编译期间传递给该语句的参数确定。

The values() method on the resultant update object is used to specify the SET conditions of the UPDATE. If left as None, the SET conditions are determined from those parameters passed to the statement during the execution and/or compilation of the statement.

where 子句是一个可选表达式,它描述了 UPDATE 语句的 WHERE 条件。

The where clause is an Optional expression describing the WHERE condition of the UPDATE statement.

以下代码片段将 students 表中的“lastname”列的值从“Khanna”更改为“Kapoor” -

Following code snippet changes value of ‘lastname’ column from ‘Khanna’ to ‘Kapoor’ in students table −

stmt = students.update().where(students.c.lastname == 'Khanna').values(lastname = 'Kapoor')

stmt 对象是一个更新对象,该对象转换为 -

The stmt object is an update object that translates to −

'UPDATE students SET lastname = :lastname WHERE students.lastname = :lastname_1'

当调用 execute() 方法时,边界参数 lastname_1 将被替换。完整的更新代码如下 -

The bound parameter lastname_1 will be substituted when execute() method is invoked. The complete update code is given below −

from sqlalchemy import create_engine, MetaData, Table, Column, Integer, String
engine = create_engine('sqlite:///college.db', echo = True)
meta = MetaData()

students = Table(
   'students',
   meta,
   Column('id', Integer, primary_key = True),
   Column('name', String),
   Column('lastname', String),
)

conn = engine.connect()
stmt=students.update().where(students.c.lastname=='Khanna').values(lastname='Kapoor')
conn.execute(stmt)
s = students.select()
conn.execute(s).fetchall()

上述代码显示了以下输出,其中第二行显示更新操作的效果,如给出的屏幕截图所示 -

The above code displays following output with second row showing effect of update operation as in the screenshot given −

[
   (1, 'Ravi', 'Kapoor'),
   (2, 'Rajiv', 'Kapoor'),
   (3, 'Komal', 'Bhandari'),
   (4, 'Abdul', 'Sattar'),
   (5, 'Priya', 'Rajhans')
]
update operation

请注意,还可以使用 sqlalchemy.sql.expression 模块中的 update() 函数来实现类似的功能,如下所示 -

Note that similar functionality can also be achieved by using update() function in sqlalchemy.sql.expression module as shown below −

from sqlalchemy.sql.expression import update
stmt = update(students).where(students.c.lastname == 'Khanna').values(lastname = 'Kapoor')

Using DELETE Expression

在上一章节中,我们已经了解了 Update 表达式的作用。我们接下来要学习的表达是 Delete

In the previous chapter, we have understood what an Update expression does. The next expression that we are going to learn is Delete.

删除操作可以通过对目标表对象运行 delete() 方法来实现,如下面的语句中所示:

The delete operation can be achieved by running delete() method on target table object as given in the following statement −

stmt = students.delete()

对于学生表,上面的代码行构造如下 SQL 表达式:

In case of students table, the above line of code constructs a SQL expression as following −

'DELETE FROM students'

但是,这将删除学生表中的所有行。通常,DELETE 查询与 WHERE 子句指定的逻辑表达式相关联。以下语句显示 where 参数:

However, this will delete all rows in students table. Usually DELETE query is associated with a logical expression specified by WHERE clause. The following statement shows where parameter −

stmt = students.delete().where(students.c.id > 2)

结果 SQL 表达式将有一个绑定参数,该参数将在语句执行时在运行时替换。

The resultant SQL expression will have a bound parameter which will be substituted at runtime when the statement is executed.

'DELETE FROM students WHERE students.id > :id_1'

以下代码示例将从学生表中删除那些姓为“Khanna”的行:

Following code example will delete those rows from students table having lastname as ‘Khanna’ −

from sqlalchemy.sql.expression import update
from sqlalchemy import create_engine, MetaData, Table, Column, Integer, String
engine = create_engine('sqlite:///college.db', echo = True)

meta = MetaData()

students = Table(
   'students', meta,
   Column('id', Integer, primary_key = True),
   Column('name', String),
   Column('lastname', String),
)

conn = engine.connect()
stmt = students.delete().where(students.c.lastname == 'Khanna')
conn.execute(stmt)
s = students.select()
conn.execute(s).fetchall()

要验证结果,请刷新 SQLiteStudio 中学生表的数据视图。

To verify the result, refresh the data view of students table in SQLiteStudio.

SQLAlchemy Core - Using Multiple Tables

RDBMS 的重要特性之一是在表之间建立关系。SQL 运算(例如 SELECT、UPDATE 和 DELETE)可以在相关表上执行。本节使用 SQLAlchemy 描述了这些运算。

One of the important features of RDBMS is establishing relation between tables. SQL operations like SELECT, UPDATE and DELETE can be performed on related tables. This section describes these operations using SQLAlchemy.

为此,在我们的 SQLite 数据库 (college.db) 中创建两个表。学生表具有与前一节中给出的相同的结构;而地址表具有使用外键约束映射到 id column in students tablest_id 列。

For this purpose, two tables are created in our SQLite database (college.db). The students table has the same structure as given in the previous section; whereas the addresses table has st_id column which is mapped to id column in students table using foreign key constraint.

以下代码将在 college.db 中创建两个表:

The following code will create two tables in college.db −

from sqlalchemy import create_engine, MetaData, Table, Column, Integer, String, ForeignKey
engine = create_engine('sqlite:///college.db', echo=True)
meta = MetaData()

students = Table(
   'students', meta,
   Column('id', Integer, primary_key = True),
   Column('name', String),
   Column('lastname', String),
)

addresses = Table(
   'addresses', meta,
   Column('id', Integer, primary_key = True),
   Column('st_id', Integer, ForeignKey('students.id')),
   Column('postal_add', String),
   Column('email_add', String))

meta.create_all(engine)

上面的代码将转换为下面显示的学生和地址表的 CREATE TABLE 查询:

Above code will translate to CREATE TABLE queries for students and addresses table as below −

CREATE TABLE students (
   id INTEGER NOT NULL,
   name VARCHAR,
   lastname VARCHAR,
   PRIMARY KEY (id)
)

CREATE TABLE addresses (
   id INTEGER NOT NULL,
   st_id INTEGER,
   postal_add VARCHAR,
   email_add VARCHAR,
   PRIMARY KEY (id),
   FOREIGN KEY(st_id) REFERENCES students (id)
)

以下屏幕截图清晰地展示了上述代码:

The following screenshots present the above code very clearly −

create table queries
addresses table queries

通过执行表对象 insert() method 填充表中的数据。要在学生表中插入 5 行,可以使用下面给出的代码:

These tables are populated with data by executing insert() method of table objects. To insert 5 rows in students table, you can use the code given below −

from sqlalchemy import create_engine, MetaData, Table, Column, Integer, String
engine = create_engine('sqlite:///college.db', echo = True)
meta = MetaData()

conn = engine.connect()
students = Table(
   'students', meta,
   Column('id', Integer, primary_key = True),
   Column('name', String),
   Column('lastname', String),
)

conn.execute(students.insert(), [
   {'name':'Ravi', 'lastname':'Kapoor'},
   {'name':'Rajiv', 'lastname' : 'Khanna'},
   {'name':'Komal','lastname' : 'Bhandari'},
   {'name':'Abdul','lastname' : 'Sattar'},
   {'name':'Priya','lastname' : 'Rajhans'},
])

Rows 在地址表中使用以下代码添加:

Rows are added in addresses table with the help of the following code −

from sqlalchemy import create_engine, MetaData, Table, Column, Integer, String
engine = create_engine('sqlite:///college.db', echo = True)
meta = MetaData()
conn = engine.connect()

addresses = Table(
   'addresses', meta,
   Column('id', Integer, primary_key = True),
   Column('st_id', Integer),
   Column('postal_add', String),
   Column('email_add', String)
)

conn.execute(addresses.insert(), [
   {'st_id':1, 'postal_add':'Shivajinagar Pune', 'email_add':'ravi@gmail.com'},
   {'st_id':1, 'postal_add':'ChurchGate Mumbai', 'email_add':'kapoor@gmail.com'},
   {'st_id':3, 'postal_add':'Jubilee Hills Hyderabad', 'email_add':'komal@gmail.com'},
   {'st_id':5, 'postal_add':'MG Road Bangaluru', 'email_add':'as@yahoo.com'},
   {'st_id':2, 'postal_add':'Cannought Place new Delhi', 'email_add':'admin@khanna.com'},
])

请注意,地址表中的 st_id 列引用学生表中的 id 列。我们现在可以使用此关系从这两个表中提取数据。我们希望从与地址表中 st_id 相对应的学生表中提取 namelastname

Note that the st_id column in addresses table refers to id column in students table. We can now use this relation to fetch data from both the tables. We want to fetch name and lastname from students table corresponding to st_id in the addresses table.

from sqlalchemy.sql import select
s = select([students, addresses]).where(students.c.id == addresses.c.st_id)
result = conn.execute(s)

for row in result:
   print (row)

选择对象将有效地转换成以下 SQL 表达式,该表达式在公共关系上联接两个表:

The select objects will effectively translate into following SQL expression joining two tables on common relation −

SELECT students.id,
   students.name,
   students.lastname,
   addresses.id,
   addresses.st_id,
   addresses.postal_add,
   addresses.email_add
FROM students, addresses
WHERE students.id = addresses.st_id

这将产生从两个表中提取相应数据的输出,如下所示:

This will produce output extracting corresponding data from both tables as follows −

(1, 'Ravi', 'Kapoor', 1, 1, 'Shivajinagar Pune', 'ravi@gmail.com')
(1, 'Ravi', 'Kapoor', 2, 1, 'ChurchGate Mumbai', 'kapoor@gmail.com')
(3, 'Komal', 'Bhandari', 3, 3, 'Jubilee Hills Hyderabad', 'komal@gmail.com')
(5, 'Priya', 'Rajhans', 4, 5, 'MG Road Bangaluru', 'as@yahoo.com')
(2, 'Rajiv', 'Khanna', 5, 2, 'Cannought Place new Delhi', 'admin@khanna.com')

Using Multiple Table Updates

在上一章中,我们讨论了如何使用多张表。因此,我们在本章中进一步学习 multiple table updates

In the previous chapter, we have discussed about how to use multiple tables. So we proceed a step further and learn multiple table updates in this chapter.

使用 SQLAlchemy 的表对象,可以在 update() 方法的 WHERE 子句中指定多个表。PostgreSQL 和 Microsoft SQL Server 支持引用多张表的 UPDATE 语句。这实现了 “UPDATE FROM” 语法,该语法一次更新一张表。但是,可以在 WHERE 子句的附加“FROM”子句中直接引用其他表。以下代码行清楚地解释了 multiple table updates 的概念。

Using SQLAlchemy’s table object, more than one table can be specified in WHERE clause of update() method. The PostgreSQL and Microsoft SQL Server support UPDATE statements that refer to multiple tables. This implements “UPDATE FROM” syntax, which updates one table at a time. However, additional tables can be referenced in an additional “FROM” clause in the WHERE clause directly. The following lines of codes explain the concept of multiple table updates clearly.

stmt = students.update().\
values({
   students.c.name:'xyz',
   addresses.c.email_add:'abc@xyz.com'
}).\
where(students.c.id == addresses.c.id)

update 对象等效于以下 UPDATE 查询:

The update object is equivalent to the following UPDATE query −

UPDATE students
SET email_add = :addresses_email_add, name = :name
FROM addresses
WHERE students.id = addresses.id

在 MySQL方言中,多个表格可以使用逗号分隔后嵌入到一个 UPDATE 语句中,如下所示:

As far as MySQL dialect is concerned, multiple tables can be embedded into a single UPDATE statement separated by a comma as given below −

stmt = students.update().\
   values(name = 'xyz').\
   where(students.c.id == addresses.c.id)

以下代码描绘了所生成的 UPDATE 查询:

The following code depicts the resulting UPDATE query −

'UPDATE students SET name = :name
FROM addresses
WHERE students.id = addresses.id'

SQLite 方言不支持 UPDATE 中的多表格条件,并显示以下错误:

SQLite dialect however doesn’t support multiple-table criteria within UPDATE and shows following error −

NotImplementedError: This backend does not support multiple-table criteria within UPDATE

Parameter-Ordered Updates

原始 SQL 的 UPDATE 查询具有 SET 子句。它由 Update.values() 构造使用原始表对象中给出的列顺序渲染。因此,具有特定列的特定 UPDATE 语句每次都会被渲染相同。由于参数本身作为 Python 词典键传递给 Update.values() 方法,因此没有其他固定的排序可用。

The UPDATE query of raw SQL has SET clause. It is rendered by the update() construct using the column ordering given in the originating Table object. Therefore, a particular UPDATE statement with particular columns will be rendered the same each time. Since the parameters themselves are passed to the Update.values() method as Python dictionary keys, there is no other fixed ordering available.

在某些情况下,SET 子句中呈现的参数顺序很重要。在 MySQL 中,提供列值更新是基于其他列值的更新。

In some cases, the order of parameters rendered in the SET clause are significant. In MySQL, providing updates to column values is based on that of other column values.

以下语句的结果:

Following statement’s result −

UPDATE table1 SET x = y + 10, y = 20

将具有与以下不同的结果:

will have a different result than −

UPDATE table1 SET y = 20, x = y + 10

MySQL 中的 SET 子句以按值方式进行评估,而不是以按行方式进行评估。为此,使用了 preserve_parameter_order 。Python 2 元组列表作为 Update.values() 方法的参数给出:

SET clause in MySQL is evaluated on a per-value basis and not on per-row basis. For this purpose, the preserve_parameter_order is used. Python list of 2-tuples is given as argument to the Update.values() method −

stmt = table1.update(preserve_parameter_order = True).\
   values([(table1.c.y, 20), (table1.c.x, table1.c.y + 10)])

列表对象类似于词典,除了它是按顺序排列的。这确保了 “y” 列的 SET 子句将首先渲染,然后是 “x” 列的 SET 子句。

The List object is similar to dictionary except that it is ordered. This ensures that the “y” column’s SET clause will render first, then the “x” column’s SET clause.

SQLAlchemy Core - Multiple Table Deletes

在本章中,我们将研究类似于使用多表更新功能的多表删除表达式。

In this chapter, we will look into the Multiple Table Deletes expression which is similar to using Multiple Table Updates function.

在许多 DBMS 方言中,DELETE 语句的 WHERE 子句中可以引用多个表格。对于 PG 和 MySQL,使用了 “DELETE USING” 语法;对于 SQL Server,使用 “DELETE FROM” 表达式引用多个表格。SQLAlchemy delete() 构造隐式支持这两种模式,通过在 WHERE 子句中指定多个表格,如下所示:

More than one table can be referred in WHERE clause of DELETE statement in many DBMS dialects. For PG and MySQL, “DELETE USING” syntax is used; and for SQL Server, using “DELETE FROM” expression refers to more than one table. The SQLAlchemy delete() construct supports both of these modes implicitly, by specifying multiple tables in the WHERE clause as follows −

stmt = users.delete().\
   where(users.c.id == addresses.c.id).\
   where(addresses.c.email_address.startswith('xyz%'))
conn.execute(stmt)

在 PostgreSQL 后端,上面语句生成的 SQL 将呈现为:

On a PostgreSQL backend, the resulting SQL from the above statement would render as −

DELETE FROM users USING addresses
WHERE users.id = addresses.id
AND (addresses.email_address LIKE %(email_address_1)s || '%%')

如果将此方法与不支持此行为的数据库一起使用,编译器将引发 NotImplementedError。

If this method is used with a database that doesn’t support this behaviour, the compiler will raise NotImplementedError.

SQLAlchemy Core - Using Joins

在本章中,我们将学习如何在 SQLAlchemy 中使用连接。

In this chapter, we will learn how to use Joins in SQLAlchemy.

连接的效果只需将两个表放在 select() 构造的 columns clausewhere clause 中即可实现。现在,我们使用 join() 和 outerjoin() 方法。

Effect of joining is achieved by just placing two tables in either the columns clause or the where clause of the select() construct. Now we use the join() and outerjoin() methods.

join() 方法从一个表对象返回一个到另一个表对象的连接对象。

The join() method returns a join object from one table object to another.

join(right, onclause = None, isouter = False, full = False)

上面代码中提到的参数的功能如下:

The functions of the parameters mentioned in the above code are as follows −

  1. right − the right side of the join; this is any Table object

  2. onclause − a SQL expression representing the ON clause of the join. If left at None, it attempts to join the two tables based on a foreign key relationship

  3. isouter − if True, renders a LEFT OUTER JOIN, instead of JOIN

  4. full − if True, renders a FULL OUTER JOIN, instead of LEFT OUTER JOIN

例如,join() 方法的以下用法将自动导致基于外键的 join。

For example, following use of join() method will automatically result in join based on the foreign key.

>>> print(students.join(addresses))

这等效于以下 SQL 表达式 −

This is equivalent to following SQL expression −

students JOIN addresses ON students.id = addresses.st_id

您可以显式提及 join 条件,如下所示 −

You can explicitly mention joining criteria as follows −

j = students.join(addresses, students.c.id == addresses.c.st_id)

如果我们现在使用此 join 构建以下选择构建,则为 −

If we now build the below select construct using this join as −

stmt = select([students]).select_from(j)

这将导致以下 SQL 表达式 −

This will result in following SQL expression −

SELECT students.id, students.name, students.lastname
FROM students JOIN addresses ON students.id = addresses.st_id

如果此语句使用表示引擎的连接执行,则将显示属于所选列的数据。完整代码如下 −

If this statement is executed using the connection representing engine, data belonging to selected columns will be displayed. The complete code is as follows −

from sqlalchemy import create_engine, MetaData, Table, Column, Integer, String, ForeignKey
engine = create_engine('sqlite:///college.db', echo = True)

meta = MetaData()
conn = engine.connect()
students = Table(
   'students', meta,
   Column('id', Integer, primary_key = True),
   Column('name', String),
   Column('lastname', String),
)

addresses = Table(
   'addresses', meta,
   Column('id', Integer, primary_key = True),
   Column('st_id', Integer,ForeignKey('students.id')),
   Column('postal_add', String),
   Column('email_add', String)
)

from sqlalchemy import join
from sqlalchemy.sql import select
j = students.join(addresses, students.c.id == addresses.c.st_id)
stmt = select([students]).select_from(j)
result = conn.execute(stmt)
result.fetchall()

以下是在上个代码中输出的 −

The following is the output of the above code −

[
   (1, 'Ravi', 'Kapoor'),
   (1, 'Ravi', 'Kapoor'),
   (3, 'Komal', 'Bhandari'),
   (5, 'Priya', 'Rajhans'),
   (2, 'Rajiv', 'Khanna')
]

SQLAlchemy Core - Using Conjunctions

联接是 SQLAlchemy 模块中的函数,它实现了 SQL 表达式的 WHERE 子句中使用的关系运算符。运算符 AND、OR、NOT 等用于形式一个复合表达式,它将两个单独的逻辑表达式相结合。在 SELECT 语句中使用 AND 的一个简单示例如下 −

Conjunctions are functions in SQLAlchemy module that implement relational operators used in WHERE clause of SQL expressions. The operators AND, OR, NOT, etc., are used to form a compound expression combining two individual logical expressions. A simple example of using AND in SELECT statement is as follows −

SELECT * from EMPLOYEE WHERE salary>10000 AND age>30

SQLAlchemy 函数 and_(), or_() and not_() 分别实现 AND、OR 和 NOT 运算符。

SQLAlchemy functions and_(), or_() and not_() respectively implement AND, OR and NOT operators.

and_() function

它生成通过 AND 连接的表达式的联接。下面给出了一个示例以便更好地理解 −

It produces a conjunction of expressions joined by AND. An example is given below for better understanding −

from sqlalchemy import and_

print(
   and_(
      students.c.name == 'Ravi',
      students.c.id <3
   )
)

这转化为 −

This translates to −

students.name = :name_1 AND students.id < :id_1

要在 students 表上的 select() 构造中使用 and_(),请使用以下代码行 −

To use and_() in a select() construct on a students table, use the following line of code −

stmt = select([students]).where(and_(students.c.name == 'Ravi', students.c.id <3))

将构建以下性质的 SELECT 语句 −

SELECT statement of the following nature will be constructed −

SELECT students.id,
   students.name,
   students.lastname
FROM students
WHERE students.name = :name_1 AND students.id < :id_1

显示以上 SELECT 查询的输出的完整代码如下 −

The complete code that displays output of the above SELECT query is as follows −

from sqlalchemy import create_engine, MetaData, Table, Column, Integer, String, ForeignKey, select
engine = create_engine('sqlite:///college.db', echo = True)
meta = MetaData()
conn = engine.connect()

students = Table(
   'students', meta,
   Column('id', Integer, primary_key = True),
   Column('name', String),
   Column('lastname', String),
)

from sqlalchemy import and_, or_
stmt = select([students]).where(and_(students.c.name == 'Ravi', students.c.id <3))
result = conn.execute(stmt)
print (result.fetchall())

假设用前一个示例中使用的数据填充 students 表,则将选择以下行 −

Following row will be selected assuming that students table is populated with data used in previous example −

[(1, 'Ravi', 'Kapoor')]

or_() function

它生成通过 OR 连接的表达式的联接。我们将使用 or_() 替换以上示例中的 stmt 对象

It produces conjunction of expressions joined by OR. We shall replace the stmt object in the above example with the following one using or_()

stmt = select([students]).where(or_(students.c.name == 'Ravi', students.c.id <3))

它将有效地等效于以下 SELECT 查询 −

Which will be effectively equivalent to following SELECT query −

SELECT students.id,
   students.name,
   students.lastname
FROM students
WHERE students.name = :name_1
OR students.id < :id_1

一旦你进行了替换并运行上述代码,结果将是符合 OR 条件的两行 −

Once you make the substitution and run the above code, the result will be two rows falling in the OR condition −

[(1, 'Ravi', 'Kapoor'),
(2, 'Rajiv', 'Khanna')]

asc() function

它生成一个 ORDER BY 升序子句。这个函数将列作为参数,以应用函数。

It produces an ascending ORDER BY clause. The function takes the column to apply the function as a parameter.

from sqlalchemy import asc
stmt = select([students]).order_by(asc(students.c.name))

以下语句实现了 SQL 表达式 −

The statement implements following SQL expression −

SELECT students.id,
   students.name,
   students.lastname
FROM students
ORDER BY students.name ASC

以下代码列出了 students 表中的所有记录,按 name 列升序排序 −

Following code lists out all records in students table in ascending order of name column −

from sqlalchemy import create_engine, MetaData, Table, Column, Integer, String, ForeignKey, select
engine = create_engine('sqlite:///college.db', echo = True)
meta = MetaData()
conn = engine.connect()

students = Table(
   'students', meta,
   Column('id', Integer, primary_key = True),
   Column('name', String),
   Column('lastname', String),
)

from sqlalchemy import asc
stmt = select([students]).order_by(asc(students.c.name))
result = conn.execute(stmt)

for row in result:
   print (row)

上述代码生成了以下输出 −

Above code produces following output −

(4, 'Abdul', 'Sattar')
(3, 'Komal', 'Bhandari')
(5, 'Priya', 'Rajhans')
(2, 'Rajiv', 'Khanna')
(1, 'Ravi', 'Kapoor')

desc() function

类似地,desc() 函数生成 ORDER BY 降序子句,如下所示 −

Similarly desc() function produces descending ORDER BY clause as follows −

from sqlalchemy import desc
stmt = select([students]).order_by(desc(students.c.lastname))

等价的 SQL 表达式是 −

The equivalent SQL expression is −

SELECT students.id,
   students.name,
   students.lastname
FROM students
ORDER BY students.lastname DESC

并且上述代码行的输出是 −

And the output for the above lines of code is −

(4, 'Abdul', 'Sattar')
(5, 'Priya', 'Rajhans')
(2, 'Rajiv', 'Khanna')
(1, 'Ravi', 'Kapoor')
(3, 'Komal', 'Bhandari')

between() function

它生成一个 BETWEEN 谓词子句。这通常用来验证特定列的值是否介于某个范围内。例如,以下代码选择 id 列介于 2 和 4 之间的行 −

It produces a BETWEEN predicate clause. This is generally used to validate if value of a certain column falls between a range. For example, following code selects rows for which id column is between 2 and 4 −

from sqlalchemy import between
stmt = select([students]).where(between(students.c.id,2,4))
print (stmt)

最终 SQL 表达式如下 −

The resulting SQL expression resembles −

SELECT students.id,
   students.name,
   students.lastname
FROM students
WHERE students.id
BETWEEN :id_1 AND :id_2

并且结果如下 −

and the result is as follows −

(2, 'Rajiv', 'Khanna')
(3, 'Komal', 'Bhandari')
(4, 'Abdul', 'Sattar')

SQLAlchemy Core - Using Functions

本章讨论了 SQLAlchemy 中使用的一些重要函数。

Some of the important functions used in SQLAlchemy are discussed in this chapter.

标准 SQL 推荐了许多大多数方言都实现的函数。它们根据传递给它的参数返回单个值。一些 SQL 函数将列作为参数,而另一些则是通用的。 Thefunc keyword in SQLAlchemy API is used to generate these functions

Standard SQL has recommended many functions which are implemented by most dialects. They return a single value based on the arguments passed to it. Some SQL functions take columns as arguments whereas some are generic. Thefunc keyword in SQLAlchemy API is used to generate these functions.

在 SQL 中,now() 是一个通用函数。以下语句使用 func 渲染 now() 函数 −

In SQL, now() is a generic function. Following statements renders the now() function using func −

from sqlalchemy.sql import func
result = conn.execute(select([func.now()]))
print (result.fetchone())

上面的代码示例结果可能如下所示 −

Sample result of above code may be as shown below −

(datetime.datetime(2018, 6, 16, 6, 4, 40),)

另一方面,count() 函数返回从表中选择的行数,由 func 的以下用法呈现 −

On the other hand, count() function which returns number of rows selected from a table, is rendered by following usage of func −

from sqlalchemy.sql import func
result = conn.execute(select([func.count(students.c.id)]))
print (result.fetchone())

从上面的代码中,将获取 students 表中行数的计数。

From the above code, count of number of rows in students table will be fetched.

使用包含以下数据的 Employee 表演示了一些内置 SQL 函数 −

Some built-in SQL functions are demonstrated using Employee table with following data −

ID

Name

Marks

1

Kamal

56

2

Fernandez

85

3

Sunil

62

4

Bhaskar

76

max() 函数通过以下使用 SQLAlchemy 中的 func 来实现,它将产生 85,即获得的总最高分 −

The max() function is implemented by following usage of func from SQLAlchemy which will result in 85, the total maximum marks obtained −

from sqlalchemy.sql import func
result = conn.execute(select([func.max(employee.c.marks)]))
print (result.fetchone())

类似地,将返回 56,即最低分数的 min() 函数将通过以下代码渲染 −

Similarly, min() function that will return 56, minimum marks, will be rendered by following code −

from sqlalchemy.sql import func
result = conn.execute(select([func.min(employee.c.marks)]))
print (result.fetchone())

因此,AVG() 函数也可以通过使用以下代码实现 −

So, the AVG() function can also be implemented by using the below code −

from sqlalchemy.sql import func
result = conn.execute(select([func.avg(employee.c.marks)]))
print (result.fetchone())

Functions are normally used in the columns clause of a select statement.
They can also be given label as well as a type. A label to function allows the result
to be targeted in a result row based on a string name, and a type is required when
you need result-set processing to occur.from sqlalchemy.sql import func

result = conn.execute(select([func.max(students.c.lastname).label('Name')]))

print (result.fetchone())

SQLAlchemy Core - Using Set Operations

在上一个章节中,我们学习了各种函数,例如 max()、min()、count() 等,在这里,我们将学习集合运算及其用法。

In the last chapter, we have learnt about various functions such as max(), min(), count(), etc., here, we will learn about set operations and their uses.

UNION 和 INTERSECT 等集合运算由标准 SQL 及其大多数方言支持。SQLAlchemy 在以下函数的帮助下实现它们 −

Set operations such as UNION and INTERSECT are supported by standard SQL and most of its dialect. SQLAlchemy implements them with the help of following functions −

union()

在组合两个或多个 SELECT 语句的结果时,UNION 会从结果集中消除重复项。表中列和数据类型的数量必须相同。

While combining results of two or more SELECT statements, UNION eliminates duplicates from the resultset. The number of columns and datatype must be same in both the tables.

union() 函数从多个表中返回 CompoundSelect 对象。以下示例演示了它的用法 −

The union() function returns a CompoundSelect object from multiple tables. Following example demonstrates its use −

from sqlalchemy import create_engine, MetaData, Table, Column, Integer, String, union
engine = create_engine('sqlite:///college.db', echo = True)

meta = MetaData()
conn = engine.connect()
addresses = Table(
   'addresses', meta,
   Column('id', Integer, primary_key = True),
   Column('st_id', Integer),
   Column('postal_add', String),
   Column('email_add', String)
)

u = union(addresses.select().where(addresses.c.email_add.like('%@gmail.com addresses.select().where(addresses.c.email_add.like('%@yahoo.com'))))

result = conn.execute(u)
result.fetchall()

联合结构转换为以下 SQL 表达式 −

The union construct translates to following SQL expression −

SELECT addresses.id,
   addresses.st_id,
   addresses.postal_add,
   addresses.email_add
FROM addresses
WHERE addresses.email_add LIKE ? UNION SELECT addresses.id,
   addresses.st_id,
   addresses.postal_add,
   addresses.email_add
FROM addresses
WHERE addresses.email_add LIKE ?

根据我们的地址表,以下行代表联合运算 −

From our addresses table, following rows represent the union operation −

[
   (1, 1, 'Shivajinagar Pune', 'ravi@gmail.com'),
   (2, 1, 'ChurchGate Mumbai', 'kapoor@gmail.com'),
   (3, 3, 'Jubilee Hills Hyderabad', 'komal@gmail.com'),
   (4, 5, 'MG Road Bangaluru', 'as@yahoo.com')
]

union_all()

UNION ALL 运算不能删除重复项,也不能对结果集中的数据进行排序。例如,在上面查询中,UNION 被 UNION ALL 替换以查看效果。

UNION ALL operation cannot remove the duplicates and cannot sort the data in the resultset. For example, in above query, UNION is replaced by UNION ALL to see the effect.

u = union_all(addresses.select().where(addresses.c.email_add.like('%@gmail.com')), addresses.select().where(addresses.c.email_add.like('%@yahoo.com')))

相应的 SQL 表达式如下 −

The corresponding SQL expression is as follows −

SELECT addresses.id,
   addresses.st_id,
   addresses.postal_add,
   addresses.email_add
FROM addresses
WHERE addresses.email_add LIKE ? UNION ALL SELECT addresses.id,
   addresses.st_id,
   addresses.postal_add,
   addresses.email_add
FROM addresses
WHERE addresses.email_add LIKE ?

except_()

SQL EXCEPT 子句/运算符用于组合两个 SELECT 语句,并返回第一个 SELECT 语句中未通过第二个 SELECT 语句返回的行。except_() 函数生成带有 EXCEPT 子句的 SELECT 表达式。

The SQL EXCEPT clause/operator is used to combine two SELECT statements and return rows from the first SELECT statement that are not returned by the second SELECT statement. The except_() function generates a SELECT expression with EXCEPT clause.

在以下示例中,except_() 函数仅从地址表中返回那些 email_add 字段中具有“gmail.com”但排除 postal_add 字段中有“Pune”的记录。

In the following example, the except_() function returns only those records from addresses table that have ‘gmail.com’ in email_add field but excludes those which have ‘Pune’ as part of postal_add field.

u = except_(addresses.select().where(addresses.c.email_add.like('%@gmail.com')), addresses.select().where(addresses.c.postal_add.like('%Pune')))

上述代码的结果是以下 SQL 表达式 −

Result of the above code is the following SQL expression −

SELECT addresses.id,
   addresses.st_id,
   addresses.postal_add,
   addresses.email_add
FROM addresses
WHERE addresses.email_add LIKE ? EXCEPT SELECT addresses.id,
   addresses.st_id,
   addresses.postal_add,
   addresses.email_add
FROM addresses
WHERE addresses.postal_add LIKE ?

假设地址表包含前面示例中使用的数据,它将显示以下输出 −

Assuming that addresses table contains data used in earlier examples, it will display following output −

[(2, 1, 'ChurchGate Mumbai', 'kapoor@gmail.com'),
   (3, 3, 'Jubilee Hills Hyderabad', 'komal@gmail.com')]

intersect()

使用 INTERSECT 运算符,SQL 在两个 SELECT 语句中显示公共行。intersect() 函数实现此行为。

Using INTERSECT operator, SQL displays common rows from both the SELECT statements. The intersect() function implements this behaviour.

在以下示例中,两个 SELECT 结构是 intersect() 函数的参数。其中一个返回包含“gmail.com”作为 email_add 列一部分的行,另一个返回包含“Pune”作为 postal_add 列一部分的行。结果将是两个结果集中的公共行。

In following examples, two SELECT constructs are parameters to intersect() function. One returns rows containing ‘gmail.com’ as part of email_add column, and other returns rows having ‘Pune’ as part of postal_add column. The result will be common rows from both resultsets.

u = intersect(addresses.select().where(addresses.c.email_add.like('%@gmail.com')), addresses.select().where(addresses.c.postal_add.like('%Pune')))

实际上,这相当于以下 SQL 语句 −

In effect, this is equivalent to following SQL statement −

SELECT addresses.id,
   addresses.st_id,
   addresses.postal_add,
   addresses.email_add
FROM addresses
WHERE addresses.email_add LIKE ? INTERSECT SELECT addresses.id,
   addresses.st_id,
   addresses.postal_add,
   addresses.email_add
FROM addresses
WHERE addresses.postal_add LIKE ?

两个绑定参数“%gmail.com”和“%Pune”从地址表中的原始数据生成一行,如下所示 −

The two bound parameters ‘%gmail.com’ and ‘%Pune’ generate a single row from original data in addresses table as shown below −

[(1, 1, 'Shivajinagar Pune', 'ravi@gmail.com')]

SQLAlchemy ORM - Declaring Mapping

SQLAlchemy 的对象关系映射器 API 的主要目标是促进将用户定义的 Python 类与数据库表相关联,并将这些类的对象与相应表中的行相关联。对象的 state 和行的 state 的变化彼此同步匹配。SQLAlchemy 能够根据用户定义的类及其定义的关系来表达数据库查询。

The main objective of the Object Relational Mapper API of SQLAlchemy is to facilitate associating user-defined Python classes with database tables, and objects of those classes with rows in their corresponding tables. Changes in states of objects and rows are synchronously matched with each other. SQLAlchemy enables expressing database queries in terms of user defined classes and their defined relationships.

ORM 基于 SQL 表达式语言构建。它是一种高级抽象的用法模式。事实上,ORM 是表达式语言的一个应用用法。

The ORM is constructed on top of the SQL Expression Language. It is a high level and abstracted pattern of usage. In fact, ORM is an applied usage of the Expression Language.

虽然可以单独使用对象关系映射器构建成功的应用程序,但有时使用 ORM 构建的应用程序可能在需要特定数据库交互时直接使用表达式语言。

Although a successful application may be constructed using the Object Relational Mapper exclusively, sometimes an application constructed with the ORM may use the Expression Language directly where specific database interactions are required.

Declare Mapping

首先,调用 create_engine() 函数来设置引擎对象,随后将其用于执行 SQL 操作。该函数有两个参数,一个是数据库名称,另一个是回显参数,当设置为 True 时将生成活动日志。如果数据库不存在,将创建该数据库。在以下示例中,创建了 SQLite 数据库。

First of all, create_engine() function is called to set up an engine object which is subsequently used to perform SQL operations. The function has two arguments, one is the name of database and other is an echo parameter when set to True will generate the activity log. If it doesn’t exist, the database will be created. In the following example, a SQLite database is created.

from sqlalchemy import create_engine
engine = create_engine('sqlite:///sales.db', echo = True)

当调用 Engine.execute() 或 Engine.connect() 等方法时,引擎将建立与数据库的真实 DBAPI 连接。然后用它来 发 射 SQLORM,它不直接使用引擎;而是由 ORM 在后台使用。

The Engine establishes a real DBAPI connection to the database when a method like Engine.execute() or Engine.connect() is called. It is then used to emit the SQLORM which does not use the Engine directly; instead, it is used behind the scenes by the ORM.

在 ORM 的情况下,配置过程首先描述数据库表,然后定义将映射到这些表的类。在 SQLAlchemy 中,这两个任务是同时执行的。这是通过使用声明系统完成的;创建的类包括描述实际数据库表的指令,这些表被映射到。

In case of ORM, the configurational process starts by describing the database tables and then by defining classes which will be mapped to those tables. In SQLAlchemy, these two tasks are performed together. This is done by using Declarative system; the classes created include directives to describe the actual database table they are mapped to.

基本类存储 Declarative 系统中类的目录和映射表。这称为声明基本类。在通常导入的模块中通常只有一个此基类的实例。declarative_base() 函数用于创建基本类。此函数在 sqlalchemy.ext.declarative 模块中定义。

A base class stores a catlog of classes and mapped tables in the Declarative system. This is called as the declarative base class. There will be usually just one instance of this base in a commonly imported module. The declarative_base() function is used to create base class. This function is defined in sqlalchemy.ext.declarative module.

from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()

声明基本类后,可以基于它定义任意数量的映射类。以下代码定义了 Customer 的类。它包含要映射到的表,以及其中的列的名称和数据类型。

Once base classis declared, any number of mapped classes can be defined in terms of it. Following code defines a Customer’s class. It contains the table to be mapped to, and names and datatypes of columns in it.

class Customers(Base):
   __tablename__ = 'customers'

   id = Column(Integer, primary_key = True)
   name = Column(String)
   address = Column(String)
   email = Column(String)

在声明性中的类必须具有 tablename 属性,以及至少一个 Column 作为主键的一部分。声明性用称为 descriptors 的特殊 Python 访问器替换所有 Column 对象。此过程称为仪器化,它提供了在 SQL 上下文中引用表的途径,并能够持久保存和加载数据库中列的值。

A class in Declarative must have a tablename attribute, and at least one Column which is part of a primary key. Declarative replaces all the Column objects with special Python accessors known as descriptors. This process is known as instrumentation which provides the means to refer to the table in a SQL context and enables persisting and loading the values of columns from the database.

这种映射类像一个普通 Python 类,根据要求具有属性和方法。

This mapped class like a normal Python class has attributes and methods as per the requirement.

声明性系统中有关类的信息称为表元数据。SQLAlchemy 使用 Table 对象来表示由声明性创建的特定表此信息。根据规范创建 Table 对象,并通过构建 Mapper 对象将其与类关联。此映射器对象不会直接使用,而是作为映射类和表之间的内部接口使用。

The information about class in Declarative system, is called as table metadata. SQLAlchemy uses Table object to represent this information for a specific table created by Declarative. The Table object is created according to the specifications, and is associated with the class by constructing a Mapper object. This mapper object is not directly used but is used internally as interface between mapped class and table.

每个 Table 对象都是名为 MetaData 的更大集合的成员,并且此对象可通过声明性基本类的 .metadata 属性获得。 MetaData.create_all() 方法是将我们的引擎作为数据库连接的源传递进去。对于尚未创建的所有表,它会向数据库发出 CREATE TABLE 语句。

Each Table object is a member of larger collection known as MetaData and this object is available using the .metadata attribute of declarative base class. The MetaData.create_all() method is, passing in our Engine as a source of database connectivity. For all tables that haven’t been created yet, it issues CREATE TABLE statements to the database.

Base.metadata.create_all(engine)

下面给出了创建数据库和表以及映射 Python 类的完整脚本 −

The complete script to create a database and a table, and to map Python class is given below −

from sqlalchemy import Column, Integer, String
from sqlalchemy import create_engine
engine = create_engine('sqlite:///sales.db', echo = True)
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()

class Customers(Base):
   __tablename__ = 'customers'
   id = Column(Integer, primary_key=True)

   name = Column(String)
   address = Column(String)
   email = Column(String)
Base.metadata.create_all(engine)

执行后,Python 控制台将回显正在执行的 SQL 表达式 −

When executed, Python console will echo following SQL expression being executed −

CREATE TABLE customers (
   id INTEGER NOT NULL,
   name VARCHAR,
   address VARCHAR,
   email VARCHAR,
   PRIMARY KEY (id)
)

如果我们使用 SQLiteStudio 图形工具打开 Sales.db,它将在其中显示具有上述结构的客户表。

If we open the Sales.db using SQLiteStudio graphic tool, it shows customers table inside it with above mentioned structure.

customers table

SQLAlchemy ORM - Creating Session

为了与数据库进行交互,我们需要获得其句柄。会话对象是数据库的句柄。使用 sessionmaker() 定义会话类 - 一个可配置的会话工厂方法,它绑定到前面创建的引擎对象。

In order to interact with the database, we need to obtain its handle. A session object is the handle to database. Session class is defined using sessionmaker() – a configurable session factory method which is bound to the engine object created earlier.

from sqlalchemy.orm import sessionmaker
Session = sessionmaker(bind = engine)

然后使用其默认构造函数设置会话对象,如下所示 −

The session object is then set up using its default constructor as follows −

session = Session()

下面列出会话类的一些经常需要的方法 −

Some of the frequently required methods of session class are listed below −

Sr.No.

Method & Description

1

begin() begins a transaction on this session

2

add() places an object in the session. Its state is persisted in the database on next flush operation

3

add_all() adds a collection of objects to the session

4

commit() flushes all items and any transaction in progress

5

delete() marks a transaction as deleted

6

execute() executes a SQL expression

7

expire() marks attributes of an instance as out of date

8

flush() flushes all object changes to the database

9

invalidate() closes the session using connection invalidation

10

rollback() rolls back the current transaction in progress

11

close() Closes current session by clearing all items and ending any transaction in progress

SQLAlchemy ORM - Adding Objects

在 SQLAlchemy ORM 的前面章节中,我们了解了如何声明映射和创建会话。在本章中,我们将了解如何向表中添加对象。

In the previous chapters of SQLAlchemy ORM, we have learnt how to declare mapping and create sessions. In this chapter, we will learn how to add objects to the table.

我们已经声明映射到 customers 表的 Customer 类。我们必须声明此类的对象,并通过会话对象的 add() 方法将其持久性地添加到表中。

We have declared Customer class that has been mapped to customers table. We have to declare an object of this class and persistently add it to the table by add() method of session object.

c1 = Sales(name = 'Ravi Kumar', address = 'Station Road Nanded', email = 'ravi@gmail.com')
session.add(c1)

请注意,此事务在使用 commit() 方法刷新之前是挂起的。

Note that this transaction is pending until the same is flushed using commit() method.

session.commit()

以下是向 customers 表中添加记录的完整脚本 -

Following is the complete script to add a record in customers table −

from sqlalchemy import Column, Integer, String
from sqlalchemy import create_engine
engine = create_engine('sqlite:///sales.db', echo = True)
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()

class Customers(Base):
   __tablename__ = 'customers'

   id = Column(Integer, primary_key=True)
   name = Column(String)
   address = Column(String)
   email = Column(String)

from sqlalchemy.orm import sessionmaker
Session = sessionmaker(bind = engine)
session = Session()

c1 = Customers(name = 'Ravi Kumar', address = 'Station Road Nanded', email = 'ravi@gmail.com')

session.add(c1)
session.commit()

要添加多条记录,我们可以使用会话类的 add_all() 方法。

To add multiple records, we can use add_all() method of the session class.

session.add_all([
   Customers(name = 'Komal Pande', address = 'Koti, Hyderabad', email = 'komal@gmail.com'),
   Customers(name = 'Rajender Nath', address = 'Sector 40, Gurgaon', email = 'nath@gmail.com'),
   Customers(name = 'S.M.Krishna', address = 'Budhwar Peth, Pune', email = 'smk@gmail.com')]
)

session.commit()

SQLiteStudio 表视图显示记录永久添加到 customers 表中。下图显示了结果 -

Table view of SQLiteStudio shows that the records are persistently added in customers table. The following image shows the result −

customers table records added

SQLAlchemy ORM - Using Query

SQLAlchemy ORM 生成的所有 SELECT 语句都是由 Query 对象构建的。它提供了一个生成接口,因此连续调用会返回一个新的 Query 对象,该对象是前一个对象的副本,并带有与其关联的其他 criteria 和 options。

All SELECT statements generated by SQLAlchemy ORM are constructed by Query object. It provides a generative interface, hence successive calls return a new Query object, a copy of the former with additional criteria and options associated with it.

Query 对象最初是使用 Session 的 query() 方法生成的,如下所示 -

Query objects are initially generated using the query() method of the Session as follows −

q = session.query(mapped class)

以下语句也相当于上述给出的语句 -

Following statement is also equivalent to the above given statement −

q = Query(mappedClass, session)

此查询对象具有 all() 方法,该方法以对象列表的形式返回结果集。如果我们对 customers 表执行它 -

The query object has all() method which returns a resultset in the form of list of objects. If we execute it on our customers table −

result = session.query(Customers).all()

此语句实际上等效于以下 SQL 表达式 -

This statement is effectively equivalent to following SQL expression −

SELECT customers.id
AS customers_id, customers.name
AS customers_name, customers.address
AS customers_address, customers.email
AS customers_email
FROM customers

结果对象可以使用 For 循环遍历,如下所示,以获取底层 customers 表中的所有记录。以下是显示 Customers 表中所有记录的完整代码 −

The result object can be traversed using For loop as below to obtain all records in underlying customers table. Here is the complete code to display all records in Customers table −

from sqlalchemy import Column, Integer, String
from sqlalchemy import create_engine
engine = create_engine('sqlite:///sales.db', echo = True)
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()

class Customers(Base):
   __tablename__ = 'customers'
   id = Column(Integer, primary_key =  True)
   name = Column(String)

   address = Column(String)
   email = Column(String)

from sqlalchemy.orm import sessionmaker
Session = sessionmaker(bind = engine)
session = Session()
result = session.query(Customers).all()

for row in result:
   print ("Name: ",row.name, "Address:",row.address, "Email:",row.email)

Python 控制台显示了如下所示的记录列表 −

Python console shows list of records as below −

Name: Ravi Kumar Address: Station Road Nanded Email: ravi@gmail.com
Name: Komal Pande Address: Koti, Hyderabad Email: komal@gmail.com
Name: Rajender Nath Address: Sector 40, Gurgaon Email: nath@gmail.com
Name: S.M.Krishna Address: Budhwar Peth, Pune Email: smk@gmail.com

Query 对象还具有以下有用的方法 −

The Query object also has following useful methods −

Sr.No.

Method & Description

1

add_columns() It adds one or more column expressions to the list of result columns to be returned.

2

add_entity() It adds a mapped entity to the list of result columns to be returned.

3

count() It returns a count of rows this Query would return.

4

delete() It performs a bulk delete query. Deletes rows matched by this query from the database.

5

distinct() It applies a DISTINCT clause to the query and return the newly resulting Query.

6

filter() It applies the given filtering criterion to a copy of this Query, using SQL expressions.

7

first() It returns the first result of this Query or None if the result doesn’t contain any row.

8

get() It returns an instance based on the given primary key identifier providing direct access to the identity map of the owning Session.

9

group_by() It applies one or more GROUP BY criterion to the query and return the newly resulting Query

10

join() It creates a SQL JOIN against this Query object’s criterion and apply generatively, returning the newly resulting Query.

11

one() It returns exactly one result or raise an exception.

12

order_by() It applies one or more ORDER BY criterion to the query and returns the newly resulting Query.

13

update() It performs a bulk update query and updates rows matched by this query in the database.

SQLAlchemy ORM - Updating Objects

在本章中,我们将了解如何使用所需的值修改或更新表。

In this chapter, we will see how to modify or update the table with desired values.

要修改任何对象的某个属性的数据,我们必须向其分配一个新值并提交更改以使更改保持持久性。

To modify data of a certain attribute of any object, we have to assign new value to it and commit the changes to make the change persistent.

让我们从主键标识符为 2 的 Customers 表中获取一个对象。我们可以按如下方式使用会话的 get() 方法 −

Let us fetch an object from the table whose primary key identifier, in our Customers table with ID=2. We can use get() method of session as follows −

x = session.query(Customers).get(2)

我们可以通过下面给出的代码来显示所选对象的内容 -

We can display contents of the selected object with the below given code −

print ("Name: ", x.name, "Address:", x.address, "Email:", x.email)

从我们的客户表中,应当显示以下输出 -

From our customers table, following output should be displayed −

Name: Komal Pande Address: Koti, Hyderabad Email: komal@gmail.com

现在我们需要通过分配新的值(如下所示)来更新地址字段 -

Now we need to update the Address field by assigning new value as given below −

x.address = 'Banjara Hills Secunderabad'
session.commit()

更改将持久地反映在数据库中。现在,我们通过如下使用 first() method 来获取表中第一行的相应对象:

The change will be persistently reflected in the database. Now we fetch object corresponding to first row in the table by using first() method as follows −

x = session.query(Customers).first()

这会执行以下 SQL 表达式:

This will execute following SQL expression −

SELECT customers.id
AS customers_id, customers.name
AS customers_name, customers.address
AS customers_address, customers.email
AS customers_email
FROM customers
LIMIT ? OFFSET ?

绑定参数分别是 LIMIT = 1 和 OFFSET = 0,这意味着将选择第一行。

The bound parameters will be LIMIT = 1 and OFFSET = 0 respectively which means first row will be selected.

print ("Name: ", x.name, "Address:", x.address, "Email:", x.email)

现在,显示第一行的上述代码的输出如下:

Now, the output for the above code displaying the first row is as follows −

Name: Ravi Kumar Address: Station Road Nanded Email: ravi@gmail.com

现在,更改名称属性,并使用下面的代码显示内容:

Now change name attribute and display the contents using the below code −

x.name = 'Ravi Shrivastava'
print ("Name: ", x.name, "Address:", x.address, "Email:", x.email)

上述代码的输出为:

The output of the above code is −

Name: Ravi Shrivastava Address: Station Road Nanded Email: ravi@gmail.com

尽管显示了更改,但未提交。你可以通过在下面的代码中使用 rollback() method 来保留早期的持久位置。

Even though the change is displayed, it is not committed. You can retain the earlier persistent position by using rollback() method with the code below.

session.rollback()

print ("Name: ", x.name, "Address:", x.address, "Email:", x.email)

将显示第一条记录的原始内容。

Original contents of first record will be displayed.

对于批量更新,我们要使用查询对象的 update() 方法。让我们尝试并给每一行(ID = 2 除外)的名称加上前缀“Mr.”。相应的 update() 语句如下:

For bulk updates, we shall use update() method of the Query object. Let us try and give a prefix, ‘Mr.’ to name in each row (except ID = 2). The corresponding update() statement is as follows −

session.query(Customers).filter(Customers.id! = 2).
update({Customers.name:"Mr."+Customers.name}, synchronize_session = False)

The update() method requires two parameters as follows −

The update() method requires two parameters as follows −

  1. A dictionary of key-values with key being the attribute to be updated, and value being the new contents of attribute.

  2. synchronize_session attribute mentioning the strategy to update attributes in the session. Valid values are false: for not synchronizing the session, fetch: performs a select query before the update to find objects that are matched by the update query; and evaluate: evaluate criteria on objects in the session.

表中四行中的三行名前会加上前缀“Mr.”。但是,不会提交更改,因此也不会反映在 SQLiteStudio 的表视图中。只有在提交会话时才会刷新。

Three out of 4 rows in the table will have name prefixed with ‘Mr.’ However, the changes are not committed and hence will not be reflected in the table view of SQLiteStudio. It will be refreshed only when we commit the session.

SQLAlchemy ORM - Applying Filter

在本章中,我们将讨论如何应用过滤器以及某些过滤器操作及其代码。

In this chapter, we will discuss how to apply filter and also certain filter operations along with their codes.

查询对象表示的结果集可以通过使用 filter() 方法来满足某些条件。filter 方法的一般用法如下:

Resultset represented by Query object can be subjected to certain criteria by using filter() method. The general usage of filter method is as follows −

session.query(class).filter(criteria)

在以下示例中,通过条件 (ID>2) 过滤了客户表上的 SELECT 查询获得的结果集:

In the following example, resultset obtained by SELECT query on Customers table is filtered by a condition, (ID>2) −

result = session.query(Customers).filter(Customers.id>2)

此语句将转换为以下 SQL 表达式:

This statement will translate into following SQL expression −

SELECT customers.id
AS customers_id, customers.name
AS customers_name, customers.address
AS customers_address, customers.email
AS customers_email
FROM customers
WHERE customers.id > ?

由于绑定参数 (?) 给定为 2,因此只会显示 ID 列 >2 的那些行。完整代码如下所示:

Since the bound parameter (?) is given as 2, only those rows with ID column>2 will be displayed. The complete code is given below −

from sqlalchemy import Column, Integer, String
from sqlalchemy import create_engine
engine = create_engine('sqlite:///sales.db', echo = True)
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()

class Customers(Base):
   __tablename__ = 'customers'

   id = Column(Integer, primary_key = True)
   name = Column(String)

   address = Column(String)
   email = Column(String)

from sqlalchemy.orm import sessionmaker
Session = sessionmaker(bind = engine)
session = Session()
result = session.query(Customers).filter(Customers.id>2)

for row in result:
   print ("ID:", row.id, "Name: ",row.name, "Address:",row.address, "Email:",row.email)

在 Python 控制台中显示的输出如下所示:

The output displayed in the Python console is as follows −

ID: 3 Name: Rajender Nath Address: Sector 40, Gurgaon Email: nath@gmail.com
ID: 4 Name: S.M.Krishna Address: Budhwar Peth, Pune Email: smk@gmail.com

SQLAlchemy ORM - Filter Operators

现在,我们将学习带有相应代码和输出的过滤操作。

Now, we will learn the filter operations with their respective codes and output.

Equals

常用的运算符是 ==,它应用一个用于检查相等性的条件。

The usual operator used is == and it applies the criteria to check equality.

result = session.query(Customers).filter(Customers.id == 2)

for row in result:
   print ("ID:", row.id, "Name: ",row.name, "Address:",row.address, "Email:",row.email)

SQLAlchemy 将发送以下 SQL 表达式:

SQLAlchemy will send following SQL expression −

SELECT customers.id
AS customers_id, customers.name
AS customers_name, customers.address
AS customers_address, customers.email
AS customers_email
FROM customers
WHERE customers.id = ?

以上代码的输出如下 −

The output for the above code is as follows −

ID: 2 Name: Komal Pande Address: Banjara Hills Secunderabad Email: komal@gmail.com

Not Equals

用于不等于运算符的运算符是 !=,它提供不等于条件。

The operator used for not equals is != and it provides not equals criteria.

result = session.query(Customers).filter(Customers.id! = 2)

for row in result:
   print ("ID:", row.id, "Name: ",row.name, "Address:",row.address, "Email:",row.email)

生成的 SQL 表达式为:

The resulting SQL expression is −

SELECT customers.id
AS customers_id, customers.name
AS customers_name, customers.address
AS customers_address, customers.email
AS customers_email
FROM customers
WHERE customers.id != ?

对于上述几行代码,输出如下所示:

The output for the above lines of code is as follows −

ID: 1 Name: Ravi Kumar Address: Station Road Nanded Email: ravi@gmail.com
ID: 3 Name: Rajender Nath Address: Sector 40, Gurgaon Email: nath@gmail.com
ID: 4 Name: S.M.Krishna Address: Budhwar Peth, Pune Email: smk@gmail.com

Like

like() 方法本身为 SELECT 表达式中的 WHERE 从句生成 LIKE 条件。

like() method itself produces the LIKE criteria for WHERE clause in the SELECT expression.

result = session.query(Customers).filter(Customers.name.like('Ra%'))
for row in result:
   print ("ID:", row.id, "Name: ",row.name, "Address:",row.address, "Email:",row.email)

上述 SQLAlchemy 代码等效于以下 SQL 表达式:

Above SQLAlchemy code is equivalent to following SQL expression −

SELECT customers.id
AS customers_id, customers.name
AS customers_name, customers.address
AS customers_address, customers.email
AS customers_email
FROM customers
WHERE customers.name LIKE ?

对于上述代码,输出为:

And the output for the above code is −

ID: 1 Name: Ravi Kumar Address: Station Road Nanded Email: ravi@gmail.com
ID: 3 Name: Rajender Nath Address: Sector 40, Gurgaon Email: nath@gmail.com

IN

此运算符检查列值是否属于列表中的项目集合。它由 in_() 方法提供。

This operator checks whether the column value belongs to a collection of items in a list. It is provided by in_() method.

result = session.query(Customers).filter(Customers.id.in_([1,3]))
for row in result:
   print ("ID:", row.id, "Name: ",row.name, "Address:",row.address, "Email:",row.email)

此处,SQLite 引擎评估的 SQL 表达式如下所示:

Here, the SQL expression evaluated by SQLite engine will be as follows −

SELECT customers.id
AS customers_id, customers.name
AS customers_name, customers.address
AS customers_address, customers.email
AS customers_email
FROM customers
WHERE customers.id IN (?, ?)

以上代码的输出如下 −

The output for the above code is as follows −

ID: 1 Name: Ravi Kumar Address: Station Road Nanded Email: ravi@gmail.com
ID: 3 Name: Rajender Nath Address: Sector 40, Gurgaon Email: nath@gmail.com

AND

此连词由 putting multiple commas separated criteria in the filter or using and_() method 生成,如下所示:

This conjunction is generated by either putting multiple commas separated criteria in the filter or using and_() method as given below −

result = session.query(Customers).filter(Customers.id>2, Customers.name.like('Ra%'))
for row in result:
   print ("ID:", row.id, "Name: ",row.name, "Address:",row.address, "Email:",row.email)
from sqlalchemy import and_
result = session.query(Customers).filter(and_(Customers.id>2, Customers.name.like('Ra%')))

for row in result:
   print ("ID:", row.id, "Name: ",row.name, "Address:",row.address, "Email:",row.email)

上述两种方法都会产生相似的 SQL 表达式:

Both the above approaches result in similar SQL expression −

SELECT customers.id
AS customers_id, customers.name
AS customers_name, customers.address
AS customers_address, customers.email
AS customers_email
FROM customers
WHERE customers.id > ? AND customers.name LIKE ?

对于上述几行代码,输出为:

The output for the above lines of code is −

ID: 3 Name: Rajender Nath Address: Sector 40, Gurgaon Email: nath@gmail.com

OR

此连词由 or_() method 实现。

This conjunction is implemented by or_() method.

from sqlalchemy import or_
result = session.query(Customers).filter(or_(Customers.id>2, Customers.name.like('Ra%')))

for row in result:
   print ("ID:", row.id, "Name: ",row.name, "Address:",row.address, "Email:",row.email)

因此,SQLite 引擎得到以下等效 SQL 表达式:

As a result, SQLite engine gets following equivalent SQL expression −

SELECT customers.id
AS customers_id, customers.name
AS customers_name, customers.address
AS customers_address, customers.email
AS customers_email
FROM customers
WHERE customers.id > ? OR customers.name LIKE ?

以上代码的输出如下 −

The output for the above code is as follows −

ID: 1 Name: Ravi Kumar Address: Station Road Nanded Email: ravi@gmail.com
ID: 3 Name: Rajender Nath Address: Sector 40, Gurgaon Email: nath@gmail.com
ID: 4 Name: S.M.Krishna Address: Budhwar Peth, Pune Email: smk@gmail.com

Returning List and Scalars

有许多查询对象的方法能够立刻发出 SQL 并返回包含已加载数据库结果的值。

There are a number of methods of Query object that immediately issue SQL and return a value containing loaded database results.

以下是返回列表和标量的简要概述 −

Here’s a brief rundown of returning list and scalars −

all()

它返回一个列表。以下是用于 all() 函数的代码行。

It returns a list. Given below is the line of code for all() function.

session.query(Customers).all()

Python 控制台显示以下发出的 SQL 表达式 −

Python console displays following SQL expression emitted −

SELECT customers.id
AS customers_id, customers.name
AS customers_name, customers.address
AS customers_address, customers.email
AS customers_email
FROM customers

first()

它应用一个限制,并以标量形式返回第一个结果。

It applies a limit of one and returns the first result as a scalar.

SELECT customers.id
AS customers_id, customers.name
AS customers_name, customers.address
AS customers_address, customers.email
AS customers_email
FROM customers
LIMIT ? OFFSET ?

LIMIT 的绑定参数为 1,而 OFFSET 的绑定参数为 0。

The bound parameters for LIMIT is 1 and for OFFSET is 0.

one()

此命令完全提取所有行,并且如果没有正好一个对象标识或结果中存在组合行,它会引发错误。

This command fully fetches all rows, and if there is not exactly one object identity or composite row present in the result, it raises an error.

session.query(Customers).one()

发现多行 −

With multiple rows found −

MultipleResultsFound: Multiple rows were found for one()

未发现行 −

With no rows found −

NoResultFound: No row was found for one()

one() 方法对于那些期望区分处理“未找到项”和“找到多项”的系统非常有用。

The one() method is useful for systems that expect to handle “no items found” versus “multiple items found” differently.

scalar()

它调用 one() 方法,并且成功后以如下方式返回行中的第一列 −

It invokes the one() method, and upon success returns the first column of the row as follows −

session.query(Customers).filter(Customers.id == 3).scalar()

这会生成以下 SQL 语句 −

This generates following SQL statement −

SELECT customers.id
AS customers_id, customers.name
AS customers_name, customers.address
AS customers_address, customers.email
AS customers_email
FROM customers
WHERE customers.id = ?

SQLAlchemy ORM - Textual SQL

早些时候使用 text() 函数的文本 SQL 已从 SQLAlchemy 的核心表达式语言的角度进行了说明。现在我们将从 ORM 的角度对此进行讨论。

Earlier, textual SQL using text() function has been explained from the perspective of core expression language of SQLAlchemy. Now we shall discuss it from ORM point of view.

可以通过指定对 text() 结构的使用灵活地将文字字符串与查询对象一起使用。大多数适用的方法都会接受它。例如,filter() 和 order_by()。

Literal strings can be used flexibly with Query object by specifying their use with the text() construct. Most applicable methods accept it. For example, filter() and order_by().

在下面给出的示例中,filter() 方法将字符串“id<3”转换成 WHERE id<3

In the example given below, the filter() method translates the string “id<3” to the WHERE id<3

from sqlalchemy import text
for cust in session.query(Customers).filter(text("id<3")):
   print(cust.name)

生成的原始 SQL 表达式显示了 filter 向 WHERE 子句的转换,其中代码如下所示 −

The raw SQL expression generated shows conversion of filter to WHERE clause with the code illustrated below −

SELECT customers.id
AS customers_id, customers.name
AS customers_name, customers.address
AS customers_address, customers.email
AS customers_email
FROM customers
WHERE id<3

从 Customers 表中的示例数据,将选择两行并将 name 列打印如下 −

From our sample data in Customers table, two rows will be selected and name column will be printed as follows −

Ravi Kumar
Komal Pande

要使用基于字符串的 SQL 指定绑定参数,请使用冒号,并且要指定值,请使用 params() 方法。

To specify bind parameters with string-based SQL, use a colon,and to specify the values, use the params() method.

cust = session.query(Customers).filter(text("id = :value")).params(value = 1).one()

Python 控制台上显示的有效 SQL 如下 −

The effective SQL displayed on Python console will be as given below −

SELECT customers.id
AS customers_id, customers.name
AS customers_name, customers.address
AS customers_address, customers.email
AS customers_email
FROM customers
WHERE id = ?

要使用完全基于字符串的语句, 可以将表示完整语句的 text() 构造传给 from_statement()。

To use an entirely string-based statement, a text() construct representing a complete statement can be passed to from_statement().

session.query(Customers).from_statement(text("SELECT * FROM customers")).all()

以上代码的结果将是一个基本的 SELECT 语句,如下所示:

The result of above code will be a basic SELECT statement as given below −

SELECT * FROM customers

很明显,将选择 customers 表中的所有记录。

Obviously, all records in customers table will be selected.

text() 结构允许我们将其文本 SQL 与核心或 ORM 映射列表达式按位置链接。我们可以通过将列表达式作为 TextClause.columns() 方法的位置参数来实现这一点。

The text() construct allows us to link its textual SQL to Core or ORM-mapped column expressions positionally. We can achieve this by passing column expressions as positional arguments to the TextClause.columns() method.

stmt = text("SELECT name, id, name, address, email FROM customers")
stmt = stmt.columns(Customers.id, Customers.name)
session.query(Customers.id, Customers.name).from_statement(stmt).all()

即使 SQLite 引擎执行 text() 方法中显示的所有列生成的以下表达式,也将选择所有行的 id 和 name 列:

The id and name columns of all rows will be selected even though the SQLite engine executes following expression generated by above code shows all columns in text() method −

SELECT name, id, name, address, email FROM customers

SQLAlchemy ORM - Building Relationship

本会话描述了如何创建另一个表,该表与我们数据库中已存在的表相关。 customers 表包含客户的主数据。我们现在需要创建一个 invoices 表,该表可能拥有属于某个客户的任意数量的发票。这是一个一对多关系的案例。

This session describes creation of another table which is related to already existing one in our database. The customers table contains master data of customers. We now need to create invoices table which may have any number of invoices belonging to a customer. This is a case of one to many relationships.

使用声明式,我们按照下面给出的方式定义此表及其映射类 Invoices:

Using declarative, we define this table along with its mapped class, Invoices as given below −

from sqlalchemy import create_engine, ForeignKey, Column, Integer, String
engine = create_engine('sqlite:///sales.db', echo = True)
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
from sqlalchemy.orm import relationship

class Customer(Base):
   __tablename__ = 'customers'

   id = Column(Integer, primary_key = True)
   name = Column(String)
   address = Column(String)
   email = Column(String)

class Invoice(Base):
   __tablename__ = 'invoices'

   id = Column(Integer, primary_key = True)
   custid = Column(Integer, ForeignKey('customers.id'))
   invno = Column(Integer)
   amount = Column(Integer)
   customer = relationship("Customer", back_populates = "invoices")

Customer.invoices = relationship("Invoice", order_by = Invoice.id, back_populates = "customer")
Base.metadata.create_all(engine)

这会向 SQLite 引擎发送一个 CREATE TABLE 查询,如下所示:

This will send a CREATE TABLE query to SQLite engine as below −

CREATE TABLE invoices (
   id INTEGER NOT NULL,
   custid INTEGER,
   invno INTEGER,
   amount INTEGER,
   PRIMARY KEY (id),
   FOREIGN KEY(custid) REFERENCES customers (id)
)

我们可以借助 SQLiteStudio 工具检查是否已在 sales.db 中创建新表。

We can check that new table is created in sales.db with the help of SQLiteStudio tool.

salesdb new table

Invoices 类对 custid 属性应用 ForeignKey 结构。此指令表明此列中的值应被限制为 customers 表中 id 列中的现有值。这是关系数据库的核心特性,并且是将不连接的表集合转换成为具有丰富的重叠关系的“粘合剂”。

Invoices class applies ForeignKey construct on custid attribute. This directive indicates that values in this column should be constrained to be values present in id column in customers table. This is a core feature of relational databases, and is the “glue” that transforms unconnected collection of tables to have rich overlapping relationships.

另一个称为 relationship() 的指令告诉 ORM 应使用属性 Invoice.customer 将 Invoice 类链接到 Customer 类。relationship() 使用两个表之间的外键关系来确定此链接的性质,并确定该关系是一对多的关系。

A second directive, known as relationship(), tells the ORM that the Invoice class should be linked to the Customer class using the attribute Invoice.customer. The relationship() uses the foreign key relationships between the two tables to determine the nature of this linkage, determining that it is many to one.

另一个 relationship() 指令会被放置在 Customer 映射类的 Customer.invoices 属性下。relationship.back_populates 参数被分配为引用互补的属性名称,以便每个 relationship() 能够就以相反的方式表达的相同关系做出明智的决策。一方是 Invoices.customer 引用 Invoices 实例,另一方是 Customer.invoices 引用 Customers 实例列表。

An additional relationship() directive is placed on the Customer mapped class under the attribute Customer.invoices. The parameter relationship.back_populates is assigned to refer to the complementary attribute names, so that each relationship() can make intelligent decision about the same relationship as expressed in reverse. On one side, Invoices.customer refers to Invoices instance, and on the other side, Customer.invoices refers to a list of Customers instances.

该关系函数是 SQLAlchemy ORM 包的 Relationship API 的一部分。它提供两个映射类之间的关系。这对应于父-子或关联表关系。

The relationship function is a part of Relationship API of SQLAlchemy ORM package. It provides a relationship between two mapped classes. This corresponds to a parent-child or associative table relationship.

以下是发现的基本关系模式−

Following are the basic Relationship Patterns found −

One To Many

一对多的关系是指通过子表上的外键来引用父表的关系。relationship() 然后指定在父表上,作为引用子表中一堆项的集合。relationship.back_populates 参数用于在一对多关系中建立双向关系,其中“反向”侧是多对一关系。

A One to Many relationship refers to parent with the help of a foreign key on the child table. relationship() is then specified on the parent, as referencing a collection of items represented by the child. The relationship.back_populates parameter is used to establish a bidirectional relationship in one-to-many, where the “reverse” side is a many to one.

Many To One

另一方面,多对一关系将外键放在父表中来引用子表。relationship() 在父表上进行声明,在那里将创建一个新的标量持有属性。这里同样使用 relationship.back_populates 参数实现双向行为。

On the other hand, Many to One relationship places a foreign key in the parent table to refer to the child. relationship() is declared on the parent, where a new scalar-holding attribute will be created. Here again the relationship.back_populates parameter is used for Bidirectionalbehaviour.

One To One

一对一关系本质上就是双向关系。uselist 标志指示在关系的“多”侧放置标量属性而不是集合。要将一对多转换为一对一关系类型,请将 uselist 参数设置为 false。

One To One relationship is essentially a bidirectional relationship in nature. The uselist flag indicates the placement of a scalar attribute instead of a collection on the “many” side of the relationship. To convert one-to-many into one-to-one type of relation, set uselist parameter to false.

Many To Many

多对多关系是通过添加与两个类相关的关联表(通过定义具有外键的属性)来建立的。它由 relationship() 的第二个参数指示。通常,表使用与声明基类关联的元数据对象,以便 ForeignKey 指令可以找到要链接的远程表。每个 relationship() 的 relationship.back_populates 参数建立双向关系。该关系的两侧都包含一个集合。

Many to Many relationship is established by adding an association table related to two classes by defining attributes with their foreign keys. It is indicated by the secondary argument to relationship(). Usually, the Table uses the MetaData object associated with the declarative base class, so that the ForeignKey directives can locate the remote tables with which to link. The relationship.back_populates parameter for each relationship() establishes a bidirectional relationship. Both sides of the relationship contain a collection.

在本章中,我们将重点关注 SQLAlchemy ORM 中的相关对象。

In this chapter, we will focus on the related objects in SQLAlchemy ORM.

现在,当我们创建一个客户对象时,Python 列表的形式将出现一个空白的账单集合。

Now when we create a Customer object, a blank invoice collection will be present in the form of Python List.

c1 = Customer(name = "Gopal Krishna", address = "Bank Street Hydarebad", email = "gk@gmail.com")

c1.invoices 的 invoices 特性将是一个空列表。我们可将列表中的项目指定为 -

The invoices attribute of c1.invoices will be an empty list. We can assign items in the list as −

c1.invoices = [Invoice(invno = 10, amount = 15000), Invoice(invno = 14, amount = 3850)]

让我们使用如下 Session 对象将此对象提交到数据库 -

Let us commit this object to the database using Session object as follows −

from sqlalchemy.orm import sessionmaker
Session = sessionmaker(bind = engine)
session = Session()
session.add(c1)
session.commit()

这将自动为客户和发票表生成 INSERT 查询 -

This will automatically generate INSERT queries for customers and invoices tables −

INSERT INTO customers (name, address, email) VALUES (?, ?, ?)
('Gopal Krishna', 'Bank Street Hydarebad', 'gk@gmail.com')
INSERT INTO invoices (custid, invno, amount) VALUES (?, ?, ?)
(2, 10, 15000)
INSERT INTO invoices (custid, invno, amount) VALUES (?, ?, ?)
(2, 14, 3850)

让我们现在在 SQLiteStudio 表视图中查看客户表和发票表的内容 -

Let us now look at contents of customers table and invoices table in the table view of SQLiteStudio −

customers table view
invoices table

您可通过使用下列命令,在构造函数中为发票提供映射的特性,来构造客户对象 -

You can construct Customer object by providing mapped attribute of invoices in the constructor itself by using the below command −

c2 = [
   Customer(
      name = "Govind Pant",
      address = "Gulmandi Aurangabad",
      email = "gpant@gmail.com",
      invoices = [Invoice(invno = 3, amount = 10000),
      Invoice(invno = 4, amount = 5000)]
   )
]

或一组待使用 add_all() 函数(如以下所示)添加的对象 -

Or a list of objects to be added using add_all() function of session object as shown below −

rows = [
   Customer(
      name = "Govind Kala",
      address = "Gulmandi Aurangabad",
      email = "kala@gmail.com",
      invoices = [Invoice(invno = 7, amount = 12000), Invoice(invno = 8, amount = 18500)]),

   Customer(
      name = "Abdul Rahman",
      address = "Rohtak",
      email = "abdulr@gmail.com",
      invoices = [Invoice(invno = 9, amount = 15000),
      Invoice(invno = 11, amount = 6000)
   ])
]

session.add_all(rows)
session.commit()

SQLAlchemy ORM - Working with Joins

现在我们有两个表,我们将了解如何同时对这两个表创建查询。要构建客户和发票之间的简单隐式连接,我们可以使用 Query.filter() 将它们的相关列等同起来。在下面,我们使用此方法一次性加载客户和发票实体 -

Now that we have two tables, we will see how to create queries on both tables at the same time. To construct a simple implicit join between Customer and Invoice, we can use Query.filter() to equate their related columns together. Below, we load the Customer and Invoice entities at once using this method −

from sqlalchemy.orm import sessionmaker
Session = sessionmaker(bind = engine)
session = Session()

for c, i in session.query(Customer, Invoice).filter(Customer.id == Invoice.custid).all():
   print ("ID: {} Name: {} Invoice No: {} Amount: {}".format(c.id,c.name, i.invno, i.amount))

SQLAlchemy 发出的 SQL 表达式如下 -

The SQL expression emitted by SQLAlchemy is as follows −

SELECT customers.id
AS customers_id, customers.name
AS customers_name, customers.address
AS customers_address, customers.email
AS customers_email, invoices.id
AS invoices_id, invoices.custid
AS invoices_custid, invoices.invno
AS invoices_invno, invoices.amount
AS invoices_amount
FROM customers, invoices
WHERE customers.id = invoices.custid

上述代码行结果如下 -

And the result of the above lines of code is as follows −

ID: 2 Name: Gopal Krishna Invoice No: 10 Amount: 15000
ID: 2 Name: Gopal Krishna Invoice No: 14 Amount: 3850
ID: 3 Name: Govind Pant Invoice No: 3 Amount: 10000
ID: 3 Name: Govind Pant Invoice No: 4 Amount: 5000
ID: 4 Name: Govind Kala Invoice No: 7 Amount: 12000
ID: 4 Name: Govind Kala Invoice No: 8 Amount: 8500
ID: 5 Name: Abdul Rahman Invoice No: 9 Amount: 15000
ID: 5 Name: Abdul Rahman Invoice No: 11 Amount: 6000

实际的 SQL JOIN 语法使用 Query.join() 方法轻松实现,如下所示 -

The actual SQL JOIN syntax is easily achieved using the Query.join() method as follows −

session.query(Customer).join(Invoice).filter(Invoice.amount == 8500).all()

连接的 SQL 表达式将显示在控制台上 -

The SQL expression for join will be displayed on the console −

SELECT customers.id
AS customers_id, customers.name
AS customers_name, customers.address
AS customers_address, customers.email
AS customers_email
FROM customers JOIN invoices ON customers.id = invoices.custid
WHERE invoices.amount = ?

我们可以使用 for 循环迭代查看结果 -

We can iterate through the result using for loop −

result = session.query(Customer).join(Invoice).filter(Invoice.amount == 8500)
for row in result:
   for inv in row.invoices:
      print (row.id, row.name, inv.invno, inv.amount)

以 8500 作为绑定参数,显示如下输出 -

With 8500 as the bind parameter, following output is displayed −

4 Govind Kala 8 8500

Query.join() 知道如何在这些表之间进行连接,因为它们之间只有一个外键。如果没有外键,或者更多外键,当使用以下任一形式时,Query.join() 能够得到更好的效果 -

Query.join() knows how to join between these tables because there’s only one foreign key between them. If there were no foreign keys, or more foreign keys, Query.join() works better when one of the following forms are used −

query.join(Invoice, id == Address.custid)

explicit condition

query.join(Customer.invoices)

specify relationship from left to right

query.join(Invoice, Customer.invoices)

same, with explicit target

query.join('invoices')

same, using a string

同样,outerjoin() 函数可用于实现左外连接。

Similarly outerjoin() function is available to achieve left outer join.

query.outerjoin(Customer.invoices)

subquery() 方法生成一个 SQL 表达式,表示嵌入在别名中的 SELECT 语句。

The subquery() method produces a SQL expression representing SELECT statement embedded within an alias.

from sqlalchemy.sql import func

stmt = session.query(
   Invoice.custid, func.count('*').label('invoice_count')
).group_by(Invoice.custid).subquery()

stmt 对象将包含一个 SQL 语句如下 -

The stmt object will contain a SQL statement as below −

SELECT invoices.custid, count(:count_1) AS invoice_count FROM invoices GROUP BY invoices.custid

一旦我们有了自己的语句,它就会表现得像 Table 结构。语句中的列可以通过一个叫做 c 的属性进行访问,如下面的代码所示:

Once we have our statement, it behaves like a Table construct. The columns on the statement are accessible through an attribute called c as shown in the below code −

for u, count in session.query(Customer, stmt.c.invoice_count).outerjoin(stmt, Customer.id == stmt.c.custid).order_by(Customer.id):
   print(u.name, count)

上面的 for 循环显示了按名称排列的账单数量,如下所示:

The above for loop displays name-wise count of invoices as follows −

Arjun Pandit None
Gopal Krishna 2
Govind Pant 2
Govind Kala 2
Abdul Rahman 2

Common Relationship Operators

在本教程中,我们将讨论基于关系构建的运算符。

In this chapter, we will discuss about the operators which build on relationships.

eq()

上面的运算符是多对一的"equals"比较。此运算符的代码行如下所示:

The above operator is a many-to-one “equals” comparison. The line of code for this operator is as shown below −

s = session.query(Customer).filter(Invoice.invno.__eq__(12))

此代码行的等效 SQL 查询如下所示:

The equivalent SQL query for the above line of code is −

SELECT customers.id
AS customers_id, customers.name
AS customers_name, customers.address
AS customers_address, customers.email
AS customers_email
FROM customers, invoices
WHERE invoices.invno = ?

ne()

此运算符是多对一的"not equals"比较。此运算符的代码行如下所示:

This operator is a many-to-one “not equals” comparison. The line of code for this operator is as shown below −

s = session.query(Customer).filter(Invoice.custid.__ne__(2))

此代码行的等效 SQL 查询如下所示:

The equivalent SQL query for the above line of code is given below −

SELECT customers.id
AS customers_id, customers.name
AS customers_name, customers.address
AS customers_address, customers.email
AS customers_email
FROM customers, invoices
WHERE invoices.custid != ?

contains()

此运算符用于一对多的集合,contains() 的代码如下所示:

This operator is used for one-to-many collections and given below is the code for contains() −

s = session.query(Invoice).filter(Invoice.invno.contains([3,4,5]))

此代码行的等效 SQL 查询如下所示:

The equivalent SQL query for the above line of code is −

SELECT invoices.id
AS invoices_id, invoices.custid
AS invoices_custid, invoices.invno
AS invoices_invno, invoices.amount
AS invoices_amount
FROM invoices
WHERE (invoices.invno LIKE '%' + ? || '%')

any()

any() 运算符用于集合,如下所示:

any() operator is used for collections as shown below −

s = session.query(Customer).filter(Customer.invoices.any(Invoice.invno==11))

此代码行的等效 SQL 查询如下所示:

The equivalent SQL query for the above line of code is shown below −

SELECT customers.id
AS customers_id, customers.name
AS customers_name, customers.address
AS customers_address, customers.email
AS customers_email
FROM customers
WHERE EXISTS (
   SELECT 1
   FROM invoices
   WHERE customers.id = invoices.custid
   AND invoices.invno = ?)

has()

此运算符用于标量引用,如下所示:

This operator is used for scalar references as follows −

s = session.query(Invoice).filter(Invoice.customer.has(name = 'Arjun Pandit'))

此代码行的等效 SQL 查询如下所示:

The equivalent SQL query for the above line of code is −

SELECT invoices.id
AS invoices_id, invoices.custid
AS invoices_custid, invoices.invno
AS invoices_invno, invoices.amount
AS invoices_amount
FROM invoices
WHERE EXISTS (
   SELECT 1
   FROM customers
   WHERE customers.id = invoices.custid
   AND customers.name = ?)

SQLAlchemy ORM - Eager Loading

渴望加载减少了查询的数量。SQLAlchemy 提供了渴望加载函数,是通过查询选项调用的,这些选项对查询提供了附加说明。这些选项通过 Query.options() 方法来确定如何加载各种属性。

Eager load reduces the number of queries. SQLAlchemy offers eager loading functions invoked via query options which give additional instructions to the Query. These options determine how to load various attributes via the Query.options() method.

Subquery Load

我们想要 Customer.invoices 能够渴望加载。orm.subqueryload() 选项提供了一个第二个 SELECT 语句,该语句完全加载了与刚刚加载的结果相关联的集合。名称“子查询”导致 SELECT 语句直接通过重复利用并作为子查询嵌入到针对相关表的 SELECT 中来构建。

We want that Customer.invoices should load eagerly. The orm.subqueryload() option gives a second SELECT statement that fully loads the collections associated with the results just loaded. The name “subquery” causes the SELECT statement to be constructed directly via the Query re-used and embedded as a subquery into a SELECT against the related table.

from sqlalchemy.orm import subqueryload
c1 = session.query(Customer).options(subqueryload(Customer.invoices)).filter_by(name = 'Govind Pant').one()

这会导致以下两个 SQL 表达式:

This results in the following two SQL expressions −

SELECT customers.id
AS customers_id, customers.name
AS customers_name, customers.address
AS customers_address, customers.email
AS customers_email
FROM customers
WHERE customers.name = ?
('Govind Pant',)

SELECT invoices.id
AS invoices_id, invoices.custid
AS invoices_custid, invoices.invno
AS invoices_invno, invoices.amount
AS invoices_amount, anon_1.customers_id
AS anon_1_customers_id
FROM (
   SELECT customers.id
   AS customers_id
   FROM customers
   WHERE customers.name = ?)

AS anon_1
JOIN invoices
ON anon_1.customers_id = invoices.custid
ORDER BY anon_1.customers_id, invoices.id 2018-06-25 18:24:47,479
INFO sqlalchemy.engine.base.Engine ('Govind Pant',)

要访问来自两个表的 data,我们可以使用下面的程序:

To access the data from two tables, we can use the below program −

print (c1.name, c1.address, c1.email)

for x in c1.invoices:
   print ("Invoice no : {}, Amount : {}".format(x.invno, x.amount))

以上程序的输出如下:

The output of the above program is as follows −

Govind Pant Gulmandi Aurangabad gpant@gmail.com
Invoice no : 3, Amount : 10000
Invoice no : 4, Amount : 5000

Joined Load

另一个函数称为 orm.joinedload()。这会发出一个 LEFT OUTER JOIN。在一步中加载了 Lead 对象以及相关对象或集合。

The other function is called orm.joinedload(). This emits a LEFT OUTER JOIN. Lead object as well as the related object or collection is loaded in one step.

from sqlalchemy.orm import joinedload
c1 = session.query(Customer).options(joinedload(Customer.invoices)).filter_by(name='Govind Pant').one()

这发出了以下表达式,给出了与上面相同的输出:

This emits following expression giving same output as above −

SELECT customers.id
AS customers_id, customers.name
AS customers_name, customers.address
AS customers_address, customers.email
AS customers_email, invoices_1.id
AS invoices_1_id, invoices_1.custid
AS invoices_1_custid, invoices_1.invno
AS invoices_1_invno, invoices_1.amount
AS invoices_1_amount

FROM customers
LEFT OUTER JOIN invoices
AS invoices_1
ON customers.id = invoices_1.custid

WHERE customers.name = ? ORDER BY invoices_1.id
('Govind Pant',)

OUTER JOIN 生成了两行,但它返回了一个 Customer 实例。这是因为查询对返回的实体应用了一种基于对象标识的“唯一化”策略。连接渴望加载可以在不影响查询结果的情况下应用。

The OUTER JOIN resulted in two rows, but it gives one instance of Customer back. This is because Query applies a “uniquing” strategy, based on object identity, to the returned entities. Joined eager loading can be applied without affecting the query results.

subqueryload() 更适合加载相关集合,而 joinedload() 更适合多对一关系。

The subqueryload() is more appropriate for loading related collections while joinedload() is better suited for many-to-one relationship.

对单个表执行删除操作很容易。你所要做的就是从会话中删除映射类的对象并提交该操作。但是,对多个相关表的删除操作有点棘手。

It is easy to perform delete operation on a single table. All you have to do is to delete an object of the mapped class from a session and commit the action. However, delete operation on multiple related tables is little tricky.

在我们的 sales.db 数据库中,Customer 和 Invoice 类被映射到客户和账单表,关系类型为一对多。我们将尝试删除 Customer 对象并查看结果。

In our sales.db database, Customer and Invoice classes are mapped to customer and invoice table with one to many type of relationship. We will try to delete Customer object and see the result.

作为快速参考,以下是 Customer 和 Invoice 类的定义:

As a quick reference, below are the definitions of Customer and Invoice classes −

from sqlalchemy import create_engine, ForeignKey, Column, Integer, String
engine = create_engine('sqlite:///sales.db', echo = True)
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
from sqlalchemy.orm import relationship
class Customer(Base):
   __tablename__ = 'customers'

   id = Column(Integer, primary_key = True)
   name = Column(String)
   address = Column(String)
   email = Column(String)

class Invoice(Base):
   __tablename__ = 'invoices'

   id = Column(Integer, primary_key = True)
   custid = Column(Integer, ForeignKey('customers.id'))
   invno = Column(Integer)
   amount = Column(Integer)
   customer = relationship("Customer", back_populates = "invoices")

Customer.invoices = relationship("Invoice", order_by = Invoice.id, back_populates = "customer")

我们设置了一个会话,并使用以下程序通过主键 ID 查询它来获取一个 Customer 对象:

We setup a session and obtain a Customer object by querying it with primary ID using the below program −

from sqlalchemy.orm import sessionmaker
Session = sessionmaker(bind=engine)
session = Session()
x = session.query(Customer).get(2)

在我们的示例表中,x.name 恰好是“Gopal Krishna”。让我们从会话中删除这个 x,并计算这个名字的出现次数。

In our sample table, x.name happens to be 'Gopal Krishna'. Let us delete this x from the session and count the occurrence of this name.

session.delete(x)
session.query(Customer).filter_by(name = 'Gopal Krishna').count()

生成的 SQL 表达式将返回 0。

The resulting SQL expression will return 0.

SELECT count(*)
AS count_1
FROM (
   SELECT customers.id
   AS customers_id, customers.name
   AS customers_name, customers.address
   AS customers_address, customers.email
   AS customers_email
   FROM customers
   WHERE customers.name = ?)
AS anon_1('Gopal Krishna',) 0

但是,x 的相关 Invoice 对象仍然存在。可以通过以下代码进行验证:

However, the related Invoice objects of x are still there. It can be verified by the following code −

session.query(Invoice).filter(Invoice.invno.in_([10,14])).count()

此处,10 和 14 是属于客户 Gopal Krishna 的发票号码。上述查询的结果为 2,这意味着相关的对象尚未被删除。

Here, 10 and 14 are invoice numbers belonging to customer Gopal Krishna. Result of the above query is 2, which means the related objects have not been deleted.

SELECT count(*)
AS count_1
FROM (
   SELECT invoices.id
   AS invoices_id, invoices.custid
   AS invoices_custid, invoices.invno
   AS invoices_invno, invoices.amount
   AS invoices_amount
   FROM invoices
   WHERE invoices.invno IN (?, ?))
AS anon_1(10, 14) 2

这是因为 SQLAlchemy 不会认为是级联删除;我们必须提供一个命令来删除它。

This is because SQLAlchemy doesn’t assume the deletion of cascade; we have to give a command to delete it.

要更改此行为,我们配置 User.addresses 关系中的级联选项。让我们关闭正在进行的会话,使用新的 declarative_base() 并重新声明 User 类,并添加包含级联配置的 addresses 关系。

To change the behavior, we configure cascade options on the User.addresses relationship. Let us close the ongoing session, use new declarative_base() and redeclare the User class, adding in the addresses relationship including the cascade configuration.

relationship 函数中的级联属性是一个级联规则的分隔列表,该列表确定会话操作应如何从父级级联到子级。默认情况下,它是错误的,这意味着它是“保存-更新,合并”。

The cascade attribute in relationship function is a comma-separated list of cascade rules which determines how Session operations should be “cascaded” from parent to child. By default, it is False, which means that it is "save-update, merge".

可用的级联如下 −

The available cascades are as follows −

  1. save-update

  2. merge

  3. expunge

  4. delete

  5. delete-orphan

  6. refresh-expire

通常使用的选项是“全部、删除孤立项”,以表示在所有情况下相关对象都应与父对象一起执行,并在取消关联时将其删除。

Often used option is "all, delete-orphan" to indicate that related objects should follow along with the parent object in all cases, and be deleted when de-associated.

因此,重新声明的 Customer 类如下所示 −

Hence redeclared Customer class is shown below −

class Customer(Base):
   __tablename__ = 'customers'

   id = Column(Integer, primary_key = True)
   name = Column(String)
   address = Column(String)
   email = Column(String)
   invoices = relationship(
      "Invoice",
      order_by = Invoice.id,
      back_populates = "customer",
      cascade = "all,
      delete, delete-orphan"
   )

让我们使用以下程序删除拥有 Gopal Krishna 名称的 Customer,并查看其关联的 Invoice 对象的数量 −

Let us delete the Customer with Gopal Krishna name using the below program and see the count of its related Invoice objects −

from sqlalchemy.orm import sessionmaker
Session = sessionmaker(bind = engine)
session = Session()
x = session.query(Customer).get(2)
session.delete(x)
session.query(Customer).filter_by(name = 'Gopal Krishna').count()
session.query(Invoice).filter(Invoice.invno.in_([10,14])).count()

使用上述脚本发出的以下 SQL,计数现在为 0 −

The count is now 0 with following SQL emitted by above script −

SELECT customers.id
AS customers_id, customers.name
AS customers_name, customers.address
AS customers_address, customers.email
AS customers_email
FROM customers
WHERE customers.id = ?
(2,)
SELECT invoices.id
AS invoices_id, invoices.custid
AS invoices_custid, invoices.invno
AS invoices_invno, invoices.amount
AS invoices_amount
FROM invoices
WHERE ? = invoices.custid
ORDER BY invoices.id (2,)
DELETE FROM invoices
WHERE invoices.id = ? ((1,), (2,))
DELETE FROM customers
WHERE customers.id = ? (2,)
SELECT count(*)
AS count_1
FROM (
   SELECT customers.id
   AS customers_id, customers.name
   AS customers_name, customers.address
   AS customers_address, customers.email
   AS customers_email
   FROM customers
   WHERE customers.name = ?)
AS anon_1('Gopal Krishna',)
SELECT count(*)
AS count_1
FROM (
   SELECT invoices.id
   AS invoices_id, invoices.custid
   AS invoices_custid, invoices.invno
   AS invoices_invno, invoices.amount
   AS invoices_amount
   FROM invoices
   WHERE invoices.invno IN (?, ?))
AS anon_1(10, 14)
0

Many to Many Relationships

两个表之间的 Many to Many relationship 通过添加一个关联表来实现,关联表有两个外键 - 一个来自每个表的主键。此外,映射到两个表的类具有一个属性,该属性具有一个集合对象的其他关联表被指定为 relationship() 函数的辅助属性。

Many to Many relationship between two tables is achieved by adding an association table such that it has two foreign keys - one from each table’s primary key. Moreover, classes mapping to the two tables have an attribute with a collection of objects of other association tables assigned as secondary attribute of relationship() function.

为此,我们将在 SQLite 数据库 (mycollege.db) 中创建两个表 - 部门和员工。这里,我们假设一个员工属于多个部门,并且一个部门有多个员工。这构成了多对多关系。

For this purpose, we shall create a SQLite database (mycollege.db) with two tables - department and employee. Here, we assume that an employee is a part of more than one department, and a department has more than one employee. This constitutes many-to-many relationship.

映射到部门和员工表的员工类和部门类的定义如下:

Definition of Employee and Department classes mapped to department and employee table is as follows −

from sqlalchemy import create_engine, ForeignKey, Column, Integer, String
engine = create_engine('sqlite:///mycollege.db', echo = True)
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
from sqlalchemy.orm import relationship

class Department(Base):
   __tablename__ = 'department'
   id = Column(Integer, primary_key = True)
   name = Column(String)
   employees = relationship('Employee', secondary = 'link')

class Employee(Base):
   __tablename__ = 'employee'
   id = Column(Integer, primary_key = True)
   name = Column(String)
   departments = relationship(Department,secondary='link')

现对 Link 类执行定义。其与关联表相连,并且分别包含分别引用部门键和员工表的主键的 department_id 和 employee_id 属性。

We now define a Link class. It is linked to link table and contains department_id and employee_id attributes respectively referencing to primary keys of department and employee table.

class Link(Base):
   __tablename__ = 'link'
   department_id = Column(
      Integer,
      ForeignKey('department.id'),
      primary_key = True)

employee_id = Column(
   Integer,
   ForeignKey('employee.id'),
   primary_key = True)

在此,我们必须注意,Department 类具有与 Employee 类相关的 employee 属性。关联功能的次要属性被分配为链接作为其值。

Here, we have to make a note that Department class has employees attribute related to Employee class. The relationship function’s secondary attribute is assigned a link as its value.

相似地,Employee 类具有与 Department 类相关的 department 属性。关联功能的次要属性被分配为链接作为其值。

Similarly, Employee class has departments attribute related to Department class. The relationship function’s secondary attribute is assigned a link as its value.

当执行以下语句时,将创建所有这三个表:

All these three tables are created when the following statement is executed −

Base.metadata.create_all(engine)

Python 控制台发出以下 CREATE TABLE 查询:

The Python console emits following CREATE TABLE queries −

CREATE TABLE department (
   id INTEGER NOT NULL,
   name VARCHAR,
   PRIMARY KEY (id)
)

CREATE TABLE employee (
   id INTEGER NOT NULL,
   name VARCHAR,
   PRIMARY KEY (id)
)

CREATE TABLE link (
   department_id INTEGER NOT NULL,
   employee_id INTEGER NOT NULL,
   PRIMARY KEY (department_id, employee_id),
   FOREIGN KEY(department_id) REFERENCES department (id),
   FOREIGN KEY(employee_id) REFERENCES employee (id)
)

我们可以通过使用 SQLiteStudio 打开 mycollege.db 来检查这一点,如下所示:

We can check this by opening mycollege.db using SQLiteStudio as shown in the screenshots given below −

department table
employee table
link table

接下来,我们创建 Department 类的三个对象和 Employee 类的三个对象,如下所示:

Next we create three objects of Department class and three objects of Employee class as shown below −

d1 = Department(name = "Accounts")
d2 = Department(name = "Sales")
d3 = Department(name = "Marketing")

e1 = Employee(name = "John")
e2 = Employee(name = "Tony")
e3 = Employee(name = "Graham")

每个表都有具有 append() 方法的集合属性。我们可以将 Employee 对象添加到 Department 对象的 Employees 集合中。相似地,我们可以将 Department 对象添加到 Employee 对象的 department 集合属性中。

Each table has a collection attribute having append() method. We can add Employee objects to Employees collection of Department object. Similarly, we can add Department objects to departments collection attribute of Employee objects.

e1.departments.append(d1)
e2.departments.append(d3)
d1.employees.append(e3)
d2.employees.append(e2)
d3.employees.append(e1)
e3.departments.append(d2)

接下来,我们需要做的就是设置一个会话对象,将所有对象添加到其中并提交修改,如下所示:

All we have to do now is to set up a session object, add all objects to it and commit the changes as shown below −

from sqlalchemy.orm import sessionmaker
Session = sessionmaker(bind = engine)
session = Session()
session.add(e1)
session.add(e2)
session.add(d1)
session.add(d2)
session.add(d3)
session.add(e3)
session.commit()

在 Python 控制台中会发出以下 SQL 语句:

Following SQL statements will be emitted on Python console −

INSERT INTO department (name) VALUES (?) ('Accounts',)
INSERT INTO department (name) VALUES (?) ('Sales',)
INSERT INTO department (name) VALUES (?) ('Marketing',)
INSERT INTO employee (name) VALUES (?) ('John',)
INSERT INTO employee (name) VALUES (?) ('Graham',)
INSERT INTO employee (name) VALUES (?) ('Tony',)
INSERT INTO link (department_id, employee_id) VALUES (?, ?) ((1, 2), (3, 1), (2, 3))
INSERT INTO link (department_id, employee_id) VALUES (?, ?) ((1, 1), (2, 2), (3, 3))

要检查上述操作的影响,请使用 SQLiteStudio 并查看部门、员工和链接表中的数据:

To check the effect of above operations, use SQLiteStudio and view data in department, employee and link tables −

department table data
employee table data
link table data

要显示数据,请运行以下查询语句:

To display the data, run the following query statement −

from sqlalchemy.orm import sessionmaker
Session = sessionmaker(bind = engine)
session = Session()

for x in session.query( Department, Employee).filter(Link.department_id == Department.id,
   Link.employee_id == Employee.id).order_by(Link.department_id).all():
   print ("Department: {} Name: {}".format(x.Department.name, x.Employee.name))

根据我们示例中填充的数据,输出将如下所述显示:

As per the data populated in our example, output will be displayed as below −

Department: Accounts Name: John
Department: Accounts Name: Graham
Department: Sales Name: Graham
Department: Sales Name: Tony
Department: Marketing Name: John
Department: Marketing Name: Tony

SQLAlchemy - Dialects

SQLAlchemy 使用方言系统与各种类型的数据库进行通信。每个数据库都有一个相应的 DBAPI 包装器。所有方言都要求安装合适的 DBAPI 驱动程序。

SQLAlchemy uses system of dialects to communicate with various types of databases. Each database has a corresponding DBAPI wrapper. All dialects require that an appropriate DBAPI driver is installed.

以下方言包含在 SQLAlchemy API 中 −

Following dialects are included in SQLAlchemy API −

  1. Firebird

  2. Microsoft SQL Server

  3. MySQL

  4. Oracle

  5. PostgreSQL

  6. SQL

  7. Sybase

一个基于 URL 的 Engine 对象是由 create_engine() 函数产生的。这些 URL 可以包括用户名、密码、主机名和数据库名称。可能存在用于附加配置的可选关键字参数。在某些情况下,文件路径是可以接受的,而在另一些情况下,“数据源名称”会替换“主机”和“数据库”部分。数据库 URL 的典型形式如下 −

An Engine object based on a URL is produced by create_engine() function. These URLs can include username, password, hostname, and database name. There may be optional keyword arguments for additional configuration. In some cases, a file path is accepted, and in others, a “data source name” replaces the “host” and “database” portions. The typical form of a database URL is as follows −

dialect+driver://username:password@host:port/database

PostgreSQL

PostgreSQL 方言使用 psycopg2 作为默认 DBAPI。pg8000 也可用作纯 Python 替代品,如下所示:

The PostgreSQL dialect uses psycopg2 as the default DBAPI. pg8000 is also available as a pure-Python substitute as shown below:

# default
engine = create_engine('postgresql://scott:tiger@localhost/mydatabase')

# psycopg2
engine = create_engine('postgresql+psycopg2://scott:tiger@localhost/mydatabase')

# pg8000
engine = create_engine('postgresql+pg8000://scott:tiger@localhost/mydatabase')

MySQL

MySQL 方言使用 mysql-python 作为默认 DBAPI。有许多可用的 MySQL DBAPI,例如 MySQL-connector-python,如下所示 −

The MySQL dialect uses mysql-python as the default DBAPI. There are many MySQL DBAPIs available, such as MySQL-connector-python as follows −

# default
engine = create_engine('mysql://scott:tiger@localhost/foo')

# mysql-python
engine = create_engine('mysql+mysqldb://scott:tiger@localhost/foo')

# MySQL-connector-python
engine = create_engine('mysql+mysqlconnector://scott:tiger@localhost/foo')

Oracle

Oracle 方言使用 cx_oracle 作为默认 DBAPI,如下所示 −

The Oracle dialect uses cx_oracle as the default DBAPI as follows −

engine = create_engine('oracle://scott:tiger@127.0.0.1:1521/sidname')
engine = create_engine('oracle+cx_oracle://scott:tiger@tnsname')

Microsoft SQL Server

SQL Server 方言使用 pyodbc 作为默认 DBAPI。pymssql 也可用。

The SQL Server dialect uses pyodbc as the default DBAPI. pymssql is also available.

# pyodbc
engine = create_engine('mssql+pyodbc://scott:tiger@mydsn')

# pymssql
engine = create_engine('mssql+pymssql://scott:tiger@hostname:port/dbname')

SQLite

SQLite 连接到基于文件的数据库,默认使用 Python 内置模块 sqlite3 。由于 SQLite 连接到本地文件,因此 URL 格式略有不同。URL 的“file”部分是数据库的文件名。对于相对文件路径,这需要三个斜杠,如下所示 −

SQLite connects to file-based databases, using the Python built-in module sqlite3 by default. As SQLite connects to local files, the URL format is slightly different. The “file” portion of the URL is the filename of the database. For a relative file path, this requires three slashes as shown below −

engine = create_engine('sqlite:///foo.db')

对于绝对文件路径,三个斜杠后面是绝对路径,如下所示 −

And for an absolute file path, the three slashes are followed by the absolute path as given below −

engine = create_engine('sqlite:///C:\\path\\to\\foo.db')

要使用 SQLite:memory:database,请指定一个空 URL,如下所示 −

To use a SQLite:memory:database, specify an empty URL as given below −

engine = create_engine('sqlite://')

Conclusion

在本教程的第一部分中,我们学习了如何使用 Expression Language 执行 SQL 语句。Expression language 将 SQL 构造嵌入 Python 代码中。在第二部分中,我们讨论了 SQLAlchemy 的对象关系映射功能。ORM API 将 SQL 表与 Python 类进行映射。

In the first part of this tutorial, we have learnt how to use the Expression Language to execute SQL statements. Expression language embeds SQL constructs in Python code. In the second part, we have discussed object relation mapping capability of SQLAlchemy. The ORM API maps the SQL tables with Python classes.