Peewee 简明教程

Peewee - Quick Guide

Peewee - Overview

Peewee 是一个 Python 对象关系映射(ORM)库,由一位位于美国软件工程师 Charles Leifer 开发于 2010 年 10 月。其最新版本是 3.13.3. Peewee 支持 SQLite、MySQL、PostgreSQL 和 Cockroach 数据库。

对象关系映射是一种编程技术,用于在面向对象编程语言中将数据在不兼容类型系统之间进行转换。

在面向对象(OO)编程语言中(例如 Python),按定义的类被认为是非标量的。它不能表示为整数和字符串等基元类型。

另一方面,Oracle、MySQL、SQLite 等数据库只能存储和处理表内组织的整数和字符串等标量值。

程序员必须将对象值转换为标量数据类型的组,以便存储在数据库中,或者在检索后将它们转换回标量值,或仅在程序内使用简单的标量值。

在 ORM 系统中,每个类都映射到基础数据库中的一个表。你无需自己编写繁琐的数据库接口代码, ORM 会处理这些问题,而你可以专注于对系统逻辑进行编程。

Environment setup

要安装 Peewee 的最新版本(由 PyPI(Python 包索引)托管),请使用 pip 安装程序。

pip3 install peewee

Peewee 工作没有任何其他依赖项。它不用安装任何其他包即可使用 SQLite,因为 sqlite3 模块与标准库捆绑在一起。

但是,要使用 MySQL 和 PostgreSQL,你可能不得不分别安装 DB-API 兼容驱动器模块 pymysql 和 pyscopg2。Cockroach 数据库通过 playhouse 扩展来处理,该扩展将随 Peewee 一起默认安装。

Peewee 是托管在 https://github.com/coleifer/peewee 存储库上的开源项目。因此,它可以通过使用 Git 从这里安装。

git clone https://github.com/coleifer/peewee.git
cd peewee
python setup.py install

Peewee - Database Class

Peewee 包中的 Database 类的一个对象表示与数据库的连接。Peewee 通过 Database 类的相应子类,为 SQLite、PostgreSQL 和 MySQL 数据库提供了开箱即用的支持。

Database 类的实例拥有打开与数据库引擎的连接所需的所有信息,并用于执行查询,管理事务和执行表的内省,列等。

Database 类拥有 SqliteDatabasePostgresqlDatabaseMySQLDatabase 子类。尽管用于 SQLite 的 DB-API 驱动器,采用 sqlite3 模块的形式,包含在 Python 的标准库中,但 psycopg2pymysql 模块必须先安装以便将 PostgreSql 和 MySQL 数据库与 Peewee 一起使用。

Using Sqlite Database

Python 在 sqlite3 模块的形式中拥有对 SQLite 数据库的内置支持。因此,它的连接非常容易。Peewee 中的 SqliteDatabase 类的对象表示连接对象。

con=SqliteDatabase(name, pragmas, timeout)

此处, pragma 是用于修改 SQLite 库操作的 SQLite 扩展。此参数可以是包含要每次打开连接时设置的 pragma 键值的一本词典或一个由 2-tuple 组成的列表。

Timeout 参数以秒为单位指定,用以设置 SQLite 驱动器的繁忙超时。这两个参数都是可选的。

下面的语句会创建一个新 SQLite 数据库的连接(如果它不存在)。

>>> db = peewee.SqliteDatabase('mydatabase.db')

Pragma 参数通常提供给新的数据库连接。pragmase 词典中提到的典型属性为 journal_modecache_sizelocking_modeforeign-keys 等。

>>> db = peewee.SqliteDatabase(
   'test.db', pragmas={'journal_mode': 'wal', 'cache_size': 10000,'foreign_keys': 1}
)

以下 pragma 设置是理想的且应指定 −

Pragma attribute

Recommended value

Meaning

journal_mode

wal

允许读者和写入者共存

cache_size

-1 * data_size_kb

设置页缓存大小(KiB)

foreign_keys

1

enforce foreign-key constraints

ignore_check_constraints

0

enforce CHECK constraints

Synchronous

0

let OS handle fsync

Peewee 还有一个 Another Python SQLite Wrapper (apsw) —— 一个高级 sqlite 驱动器。它提供虚拟表和文件系统以及共享连接等高级功能。APSW 比标准库 sqlite3 模块更快。

Peewee - Model

Peewee API 中 Model 子类的一个对象对应一个表,该表位于已建立其连接的数据库中。它允许执行数据库表操作,通过 Model 类中定义的方法来实现。

一个用户定义的 Model 有一个或更多类属性,它们中的每一个都是 Field 类的对象。Peewee 有多个子类用于保存不同类型的数据。示例有 TextField、DatetimeField 等。它们对应于数据库表中的字段或列。关联的数据库和表以及模型配置的引用在 Meta 类中提及。以下属性用于指定配置 −

Meta class Attributes

下面解释了元类属性:

Sr.No

Attribute & Description

1

Database Database for model.

2

db_table 用于存储数据的表的名称。默认情况下,它是模型类的名称。

3

Indexes 一组列出要编入索引的字段。

4

primary_key A composite key instance.

5

Constraints 表约束的列表。

6

Schema 模型的数据库模式。

7

Temporary Indicate temporary table.

8

depends_on 指出此表取决于另一个表才能创建。

9

without_rowid 指明表不应有 rowid(仅限 SQLite)。

以下代码为 mydatabase.db 中的 User 表定义了 Model 类 -

from peewee import *
db = SqliteDatabase('mydatabase.db')
class User (Model):
   name=TextField()
   age=IntegerField()
   class Meta:
      database=db
      db_table='User'
User.create_table()

create_table() 方法是 Model 类的一个类方法,它执行等效的 CREATE TABLE 查询。另一个实例方法 save() 添加了一个与对象对应行。

from peewee import *
db = SqliteDatabase('mydatabase.db')
class User (Model):
   name=TextField()
   age=IntegerField()
   class Meta:
      database=db
      db_table='User'

User.create_table()
rec1=User(name="Rajesh", age=21)
rec1.save()

Methods in Model class

Model 类中的其他方法如下所示 −

Sr.No

Model Class & Description

1

Classmethod alias() 创建模型类的别名。它允许在查询中多次引用同一个 Model。

2

Classmethod select() 执行 SELECT 查询操作。如果没有明确提供字段作为参数,则该查询默认与 SELECT * 等效。

3

Classmethod update() 执行 UPDATE 查询函数。

4

classmethod insert() 在映射到模型的底表中插入新行。

5

classmethod delete() 执行删除查询,通常与 where 子句的过滤器相关联。

6

classmethod get() 从与给定过滤器匹配的映射表中检索单个行。

7

get_id() 实例方法返回行的主键。

8

save() 将对象的数据另存为新行。如果主键值已存在,它将导致执行 UPDATE 查询。

9

classmethod bind() 将模型绑定到给定的数据库。

Peewee - Field Class

Model 类包含一个或多个属性,它们是 Peewee 中 Field 类的对象。不会直接实例化 Base Field 类。Peewee 为等效 SQL 数据类型定义不同的子类。

Field 类的构造函数具有以下参数−

Sr.No

Constructor & Description

1

column_name (str) 为字段指定列名。

2

primary_key (bool) 字段为主键。

3

constraints (list) 用于应用列约束的列表

4

choices (list) 将列值映射到显示标签的可迭代 2 元组。

5

null (bool) Field allows NULLs.

6

index (bool) 在字段中创建索引。

7

unique (bool) 在字段中创建唯一索引。

8

Default Default value.

9

collation (str) 字段的校对名称。

10

help_text (str) 字段的帮助文本,元数据用途。

11

verbose_name (str) 字段的详细名称,元数据用途。

Field 类的子类映射到不同数据库(即 SQLite、PostgreSQL、MySQL 等)中对应的数据库类型。

Numeric Field classes

Peewee 中的数字字段类如下 −

Sr.No

Field classes & Description

1

IntegerField 用于存储整数的字段类。

2

BigIntegerField 用于存储大整数的字段类(分别在 SQLite、PostgreSQL 和 MySQL 中映射到 integer、bigint 和 bigint 类型)。

3

SmallIntegerField 用于存储小整数的字段类(如果数据库支持)。

4

FloatField 用于存储浮点数的字段类对应于实际数据类型。

5

DoubleField 用于存储双精度浮点数的字段类映射到对应 SQL 数据库中的等效数据类型。

6

DecimalField 用于存储十进制数的字段类。参数如下 −max_digits(int)- 要存储的最大数字。decimal_places(int)- 最大精度。auto_round(bool)- 自动舍入值。

Text fields

Peewee 中的文本字段如下 −

Sr.No

Fields & Description

1

CharField 用于存储字符串的字段类。最大 255 个字符。等效的 SQL 数据类型为 varchar。

2

FixedCharField 用于存储固定长度字符串的字段类。

3

TextField 用于存储文本的字段类。映射到 SQLite 和 PostgreSQL 中的 TEXT 数据类型,以及 MySQL 中的 longtext。

Binary fields

下面是对 Peewee 中二进制字段的说明−

Sr.No

Fields & Description

1

BlobField 用于存储二进制数据的字段类。

2

BitField 用于在 64 位整数列中存储选项的字段类。

3

BigBitField 用于在二进制大对象 (BLOB) 中存储任意大小位图的字段类。该字段将根据需要扩充底层缓冲区。

4

UUIDField 用于存储通用唯一标识符 (UUID) 对象的字段类。映射到 Postgres 中的 UUID 类型。SQLite 和 MySQL 没有 UUID 类型,它存储为 VARCHAR。

Date and Time fields

Peewee 中的日期和时间字段如下所示 −

Sr.No

Fields & Description

1

DateTimeField 用于存储 datetime.datetime 对象的字段类。接受特殊参数字符串格式,其中可以对 datetime 进行编码。

2

DateField 用于存储 datetime.date 对象的字段类。接受特殊参数字符串格式进行日期编码。

3

TimeField 用于存储 datetime.time 对象的字段类。接受特殊参数格式以显示编码时间。

由于 SQLite 没有 DateTime 数据类型,所以此字段被映射为字符串。

ForeignKeyField

此类用于在两个模型中建立外键关系,这样就得到了数据库中相应的表。此类使用以下参数进行实例化 −

Sr.No

Fields & Description

1

model (Model) 要引用的模型。如果设置为“self”,它是一个自引用外键。

2

field (Field) 要引用的模型字段(默认为主键)。

3

backref (str) 反向引用的访问器名称。“+”禁用反向引用访问器。

4

on_delete (str) ON DELETE action.

5

on_update (str) ON UPDATE action.

6

lazy_load (bool) 当访问外键字段属性时获取相关对象。如果为 FALSE,则访问外键字段会返回存储在外键列中的值。

Example

以下是 ForeignKeyField 的示例。

from peewee import *

db = SqliteDatabase('mydatabase.db')
class Customer(Model):
   id=IntegerField(primary_key=True)
   name = TextField()
   address = TextField()
   phone = IntegerField()
   class Meta:
      database=db
      db_table='Customers'

class Invoice(Model):
   id=IntegerField(primary_key=True)
   invno=IntegerField()
   amount=IntegerField()
   custid=ForeignKeyField(Customer, backref='Invoices')
   class Meta:
      database=db
      db_table='Invoices'

db.create_tables([Customer, Invoice])

执行上述脚本时,将运行以下 SQL 查询 −

CREATE TABLE Customers (
   id INTEGER NOT NULL
   PRIMARY KEY,
   name TEXT NOT NULL,
   address TEXT NOT NULL,
   phone INTEGER NOT NULL
);
CREATE TABLE Invoices (
   id INTEGER NOT NULL
   PRIMARY KEY,
   invno INTEGER NOT NULL,
   amount INTEGER NOT NULL,
   custid_id INTEGER NOT NULL,
   FOREIGN KEY (
      custid_id
   )
   REFERENCES Customers (id)
);

在 SQLiteStudio 图形用户界面工具中验证时,表结构如下所示 −

foreignkey field
sqlitestuidio gui tool

Other Field Types

Peewee 中其他字段类型包括−

Sr.No

Fields & Description

1

IPField 用于高效存储 IPv4 地址(作为整数)的 Field 类。

2

BooleanField 用于存储布尔值的 Field 类。

3

AutoField 用于存储自动增量主键的 Field 类。

4

IdentityField 用于存储使用新 Postgres 10 IDENTITY 用于存储使用新 Postgres 10 IDENTITY 列类型存储自增主键的 Field 类。列类型。

Peewee - Insert a New Record

在 Peewee 中,有多个命令可以通过这些命令,可以在表中添加新的记录。我们已经使用了 Model 实例的 save() 方法。

rec1=User(name="Rajesh", age=21)
rec1.save()

Peewee.Model 类还具有 a create() 方法,该方法创建一个新实例并在表中添加其数据。

User.create(name="Kiran", age=19)

除此之外,Model 还具有 insert() 作为构造 SQL insert 查询对象的类方法。查询对象的 execute() 方法执行为基础表中的行添加操作。

q = User.insert(name='Lata', age=20)
q.execute()

查询对象相当于 INSERT 查询。q.sql() 返回查询字符串。

print (q.sql())
('INSERT INTO "User" ("name", "age") VALUES (?, ?)', ['Lata', 20])

以下是演示使用上述插入记录方式的完整代码。

from peewee import *
db = SqliteDatabase('mydatabase.db')
class User (Model):
   name=TextField()
   age=IntegerField()
   class Meta:
      database=db
      db_table='User'

db.create_tables([User])
rec1=User(name="Rajesh", age=21)
rec1.save()
a=User(name="Amar", age=20)
a.save()
User.create(name="Kiran", age=19)
q = User.insert(name='Lata', age=20)
q.execute()
db.close()

我们可以在 SQLiteStudio GUI 中验证结果。

sqlitestuidio gui

Bulk Inserts

为了在表中一次使用多行,Peewee 提供两种方法: bulk_create 和 insert_many。

insert_many()

insert_many() 方法生成等效 INSERT 查询,使用字典对象列表,每个列表都具有一个对象的一个字段值对。

rows=[{"name":"Rajesh", "age":21}, {"name":"Amar", "age":20}]
q=User.insert_many(rows)
q.execute()

这里也是如此,q.sql() 返回 INSERT 查询字符串,如下获取 −

print (q.sql())
('INSERT INTO "User" ("name", "age") VALUES (?, ?), (?, ?)', ['Rajesh', 21, 'Amar', 20])

bulk_create()

此方法采用一个列表参数,其中包含映射到表的模型的一个或多个未保存实例。

a=User(name="Kiran", age=19)
b=User(name='Lata', age=20)
User.bulk_create([a,b])

以下代码使用两种方法执行批量插入操作。

from peewee import *
db = SqliteDatabase('mydatabase.db')
class User (Model):
   name=TextField()
   age=IntegerField()
   class Meta:
      database=db
      db_table='User'

db.create_tables([User])
rows=[{"name":"Rajesh", "age":21}, {"name":"Amar", "age":20}]
q=User.insert_many(rows)
q.execute()
a=User(name="Kiran", age=19)
b=User(name='Lata', age=20)
User.bulk_create([a,b])
db.close()

Peewee - Select Records

从表中检索数据的最简单也是最明显的方法是调用相应模型的 select() 方法。在 select() 方法内部,我们可以指定一个或多个字段属性。但是,如果没有指定,则会选择所有列。

Model.select() 返回与行对应的模型实例列表。这类似于 SELECT 查询返回的结果集,可以通过 for 循环遍历该结果集。

from peewee import *
db = SqliteDatabase('mydatabase.db')
class User (Model):
   name=TextField()
   age=IntegerField()
   class Meta:
      database=db
      db_table='User'
rows=User.select()
print (rows.sql())
for row in rows:
   print ("name: {} age: {}".format(row.name, row.age))
db.close()

上述脚本显示以下输出 -

('SELECT "t1"."id", "t1"."name", "t1"."age" FROM "User" AS "t1"', [])
name: Rajesh age: 21
name: Amar age  : 20
name: Kiran age : 19
name: Lata age  : 20

Peewee - Filters

可以使用 where 子句从 SQLite 表中检索数据。Peewee 支持以下逻辑运算符列表。

==

x equals y

<

x 小于 y

x 小于或等于 y

>

x 大于 y

>=

x 大于或等于 y

!=

x 不等于 y

<<

x IN y,其中 y 为列表或查询

>>

x IS y,其中 y 为 None/NULL

%

x LIKE y,其中 y 可能包含通配符

**

x ILIKE y,其中 y 可能包含通配符

^

x XOR y

~

一元否定(例如,NOT x)

以下代码显示 age>=20: 的名称

rows=User.select().where (User.age>=20)
for row in rows:
   print ("name: {} age: {}".format(row.name, row.age))

以下代码仅显示名称列表中存在的名称。

names=['Anil', 'Amar', 'Kiran', 'Bala']
rows=User.select().where (User.name << names)
for row in rows:
   print ("name: {} age: {}".format(row.name, row.age))

Peewee 因此生成的 SELECT 查询将为 −

('SELECT "t1"."id", "t1"."name", "t1"."age" FROM "User" AS "t1" WHERE
   ("t1"."name" IN (?, ?, ?, ?))', ['Anil', 'Amar', 'Kiran', 'Bala'])

结果输出将如下所示 −

name: Amar age: 20
name: Kiran age: 19

Filtering Methods

除了核心 Python 中定义的上述逻辑运算符之外,Peewee 还提供以下方法进行筛选 −

Sr.No

Methods & Description

1

.in_(value) IN 查找(与 << 标识)。

2

.not_in(value) NOT IN lookup.

3

.is_null(is_null) IS NULL 或 IS NOT NULL。接受布尔参数。

4

.contains(substr) Wild-card search for substring.

5

.startswith(prefix) 搜索以前缀开头的值。

6

.endswith(suffix) 以某个后缀结尾的值搜索。

7

.between(low, high) 在低和高之间值搜索。

8

.regexp(exp) Regular expression match (case-sensitive).

9

.iregexp(exp) Regular expression match (case-insensitive).

10

.bin_and(value) Binary AND.

11

.bin_or(value) Binary OR.

12

.concat(other) 使用连接两个字符串或者对象。

.

13

.distinct() 标记列以作 DISTINCT 选择。

14

.collate(collation) 指定具有给定排序规则的列。

15

.cast(type) 将列的值转换为给定类型。

作为以上方法的一个例子,查看以下代码。它获取以“R”开头或以“r”结尾的名称。

rows=User.select().where (User.name.startswith('R') | User.name.endswith('r'))

等价的 SQL SELECT 查询为:

('SELECT "t1"."id", "t1"."name", "t1"."age" FROM "User" AS "t1" WHERE
   (("t1"."name" LIKE ?) OR ("t1"."name" LIKE ?))', ['R%', '%r'])

Alternatives

Python 内置的运算符 in、not in、and、or 等不起作用。相反,使用 Peewee 替代。

您可以使用 −

  1. .in_() and .not_in() methods instead of in and not in operators.

  2. & instead of and.

  3. | instead of or.

  4. ~ instead of not.

  5. .is_null() instead of is.

  6. None or == None.

Peewee - Primary and Composite Keys

建议关系数据库中的表应当具有一个应用了主键约束的列。相应地,Peewee 模型类也可以指定具有 primary-key 设置为 True 的字段属性。但是,如果模型类没有任何主键,Peewee 自动创建一个,名称为 “id”。请注意,上面定义的 User 模型没有任何明确定义为主键的字段。因此,我们在数据库中映射的 User 表具有一个 id 字段。

要定义一个自动递增整型主键,使用 AutoField 对象作为模型中的一个属性。

class User (Model):
   user_id=AutoField()
   name=TextField()
   age=IntegerField()
   class Meta:
      database=db
      db_table='User'

该内容将转换为以下 CREATE TABLE 查询 −

CREATE TABLE User (
   user_id INTEGER NOT NULL
   PRIMARY KEY,
   name TEXT NOT NULL,
   age INTEGER NOT NULL
);

您还可以通过将 primary_key 参数设置为 True,来将任何非整型字段分配为一个主键。假设我们想将某个字母数字值作为 user_id 存储。

class User (Model):
   user_id=TextField(primary_key=True)
   name=TextField()
   age=IntegerField()
   class Meta:
      database=db
      db_table='User'

但是,当模型包含非整型字段作为主键时,模型实例的 save() 方法不会导致数据库驱动程序自动生成新的 ID,因此我们需要传递 force_insert=True 参数。但是,请注意 create() 方法隐式指定 force_insert 参数。

User.create(user_id='A001',name="Rajesh", age=21)
b=User(user_id='A002',name="Amar", age=20)
b.save(force_insert=True)

save() 方法还会更新表中现有的行,在这种情况,强制插入主键是没有必要的,因为具有唯一主键的 ID 已存在。

Peewee 允许定义复合主键的功能。 CompositeKey 类的对象在 Meta 类中定义为主键。在以下示例中,由 User 模型的 name 和 city 字段组成的复合键已被分配为复合键。

class User (Model):
   name=TextField()
   city=TextField()
   age=IntegerField()
   class Meta:
      database=db
      db_table='User'
      primary_key=CompositeKey('name', 'city')

此模型转换为以下 CREATE TABLE 查询。

CREATE TABLE User (
   name TEXT NOT NULL,
   city TEXT NOT NULL,
   age INTEGER NOT NULL,
   PRIMARY KEY (
      name,
      city
   )
);

如果您希望该表没有主键,可在模型的 Meta 类中指定 primary_key=False。

Peewee - Update Existing Records

可以调用模型实例或 update() 类方法上的 save() 方法来修改现有数据。

以下示例在 get() 方法的帮助下从 User 表中提取一行,并通过更改 age 字段的值来更新它。

row=User.get(User.name=="Amar")
print ("name: {} age: {}".format(row.name, row.age))
row.age=25
row.save()

方法类的 update() 方法生成 UPDATE 查询。然后调用查询对象的 execute() 方法。

以下示例使用 update() 方法来更改其中 age 列 >20 的行的 age 列。

qry=User.update({User.age:25}).where(User.age>20)
print (qry.sql())
qry.execute()

update() 方法呈现的 SQL 查询如下 −

('UPDATE "User" SET "age" = ? WHERE ("User"."age" > ?)', [25, 20])

Peewee 还有 bulk_update() 方法,以帮助在单个查询操作中更新多个模型实例。该方法需要更新的模型对象以及要更新的字段列表。

以下示例通过新值更新指定行的 age 字段。

rows=User.select()
rows[0].age=25
rows[2].age=23
User.bulk_update([rows[0], rows[2]], fields=[User.age])

Peewee - Delete Records

对模型实例运行 delete_instance() 方法,从映射的表中删除相应行。

obj=User.get(User.name=="Amar")
obj.delete_instance()

另一方面,delete() 是模型类中定义的类方法,它生成 DELETE 查询。有效执行它从表中删除行。

db.create_tables([User])
qry=User.delete().where (User.age==25)
qry.execute()

数据库中受影响的表显示了 DELETE 查询的效果,如下所示 −

('DELETE FROM "User" WHERE ("User"."age" = ?)', [25])

Peewee - Create Index

通过使用 Peewee ORM,可以定义一个模型,它将在单个列和多列上创建带有索引的表。

根据字段属性定义,将唯一约束设置为 True 将在映射的字段上创建索引。同样,将 index=True 参数传递给字段构造函数也会在指定字段上创建索引。

在以下示例中,MyUser 模型中有两个字段,其中 username 字段具有设置为 True 的唯一参数,而 email 字段则有 index=True

class MyUser(Model):
   username = CharField(unique=True)
   email = CharField(index=True)
   class Meta:
      database=db
      db_table='MyUser'

因此,SQLiteStudio 图形用户界面 (GUI) 会显示如下创建的索引 −

sqlitestudio graphical user interface

为了定义一个多列索引,我们需要在模型类的定义中,在 Meta 类中添加 indexes 属性。这组成的 2 项元组序列,一项元组定义一个索引。在每个 2 元素元组中,第一部分是字段名称组成的元组,第二部分设置为 True 以使其唯一,否则为 False。

我们按如下方式使用一个两列唯一索引定义 MyUser 模型 −

class MyUser (Model):
   name=TextField()
   city=TextField()
   age=IntegerField()
   class Meta:
      database=db
      db_table='MyUser'
      indexes=(
         (('name', 'city'), True),
      )

因此,SQLiteStudio 显示的索引定义如下面的图 −

sqlitestudio my user

索引也可以在模型定义外构建。

您还可以通过手动提供 SQL 帮助器语句作为 add_index() 方法的参数来创建索引。

MyUser.add_index(SQL('CREATE INDEX idx on MyUser(name);'))

当使用 SQLite 时,特别需要上面的方法。对于 MySQL 和 PostgreSQL,我们可以获取索引对象并将其用于 add_index() 方法。

ind=MyUser.index(MyUser.name)
MyUser.add_index(ind)

Peewee - Constraints

约束是对字段中可以放入的可能值施加的限制。其中一个约束是主键。当在字段定义中指定 primary_key=True 时,每一行只能存储唯一的值 - 字段的同一值不能在另一行中重复出现。

如果某个字段不是主键,仍然可以对其施加约束,以在表中存储 unique 值。字段构造器还具有约束参数。

下面的示例在 age 字段上应用 CHECK 约束。

class MyUser (Model):
   name=TextField()
   city=TextField()
   age=IntegerField(constraints=[Check('name<10')])
   class Meta:
      database=db
      db_table='MyUser'

这将生成以下数据定义语言 (DDL) 表达式 -

CREATE TABLE MyUser (
   id INTEGER NOT NULL
   PRIMARY KEY,
   name TEXT NOT NULL,
   city TEXT NOT NULL,
   age INTEGER NOT NULL
   CHECK (name < 10)
);

因此,如果新行中 age<10 将导致错误。

MyUser.create(name="Rajesh", city="Mumbai",age=9)
peewee.IntegrityError: CHECK constraint failed: MyUser

在字段定义中,我们还可以使用 DEFAULT 约束,如下面的 city 字段定义。

city=TextField(constraints=[SQL("DEFAULT 'Mumbai'")])

因此,可以构建带有或不带有 city 明确值的对象模型。如果没有使用,city 字段将由默认值 - Mumbai 填充。

Peewee - Using MySQL

如前所述,Peewee 通过 MySQLDatabase 类支持 MySQL 数据库。但是,与 SQLite 数据库不同,Peewee 无法创建 MySql 数据库。您需要使用诸如 pymysql 之类的 DB-API 兼容模块的功能手动创建它或使用此类模块。

首先,您应该在机器中安装 MySQL 服务器。它可以是从 https://dev.mysql.com/downloads/installer/. 安装的独立 MySQL 服务器

您还可以使用捆绑了 MySQL 的 Apache(例如从 https://www.apachefriends.org/download.html 下载并安装的 XAMPP )。

接下来,我们安装 pymysql 模块,它是与 DB-API 兼容的 Python 驱动程序。

pip install pymysql

创建一个名为 mydatabase 的新数据库。我们将使用 XAMPP 中提供的 phpmyadmin 接口。

my databases

如果您选择以编程方式创建数据库,请使用以下 Python 脚本 -

import pymysql

conn = pymysql.connect(host='localhost', user='root', password='')
conn.cursor().execute('CREATE DATABASE mydatabase')
conn.close()

在服务器上创建数据库后,我们现在可以声明模型,从而在其中创建映射表。

MySQLDatabase 对象需要服务器凭据,例如主机、端口、用户名和密码。

from peewee import *
db = MySQLDatabase('mydatabase', host='localhost', port=3306, user='root', password='')
class MyUser (Model):
   name=TextField()
   city=TextField(constraints=[SQL("DEFAULT 'Mumbai'")])
   age=IntegerField()
   class Meta:
      database=db
      db_table='MyUser'
db.connect()
db.create_tables([MyUser])

PhpMyAdmin Web 界面现在显示了已创建的 myuser 表。

phpmyadmin

Peewee - Using PostgreSQL

Peewee 也支持 PostgreSQL 数据库。它为该目的提供了 PostgresqlDatabase 类。在本章中,我们将看到如何借助 Peewee 模型连接到 Postgres 数据库并在其中创建一个表。

就像 MySQL 的情况一样,使用 Peewee 的功能无法在 Postgres 服务器上创建数据库。必须使用 Postgres shell 或 PgAdmin 工具手动创建数据库。

首先,我们需要安装 Postgres 服务器。对于 Windows 操作系统,我们可以下载 https://get.enterprisedb.com/postgresql/postgresql-13.1-1-windows-x64.exe 并安装它。

接下来,使用 pip 安装程序安装 Postgres 的 Python 驱动程序,即 Psycopg2 软件包。

pip install psycopg2

然后,从 PgAdmin 工具或 psql shell 启动服务器。我们现在可以创建数据库。运行以下 Python 脚本可在 Postgres 服务器上创建 mydatabase。

import psycopg2

conn = psycopg2.connect(host='localhost', user='postgres', password='postgres')
conn.cursor().execute('CREATE DATABASE mydatabase')
conn.close()

检查数据库是否已创建。在 psql shell 中,可以使用 \l 命令验证其是否已创建:

list of databases

要声明 MyUser 模型并在上述数据库中创建同名表,请运行以下 Python 代码:

from peewee import *

db = PostgresqlDatabase('mydatabase', host='localhost', port=5432, user='postgres', password='postgres')
class MyUser (Model):
   name=TextField()
   city=TextField(constraints=[SQL("DEFAULT 'Mumbai'")])
   age=IntegerField()
   class Meta:
      database=db
      db_table='MyUser'

db.connect()
db.create_tables([MyUser])

我们可以验证表是否已创建。在 shell 内,连接到 mydatabase 并获取其中的表列表。

mydatabase

要检查新创建的 MyUser 数据库的结构,请在 shell 中运行以下查询。

myuser database

Peewee - Defining Database Dynamically

如果你的数据库计划在运行时发生变化,请使用 DatabaseProxy 帮助程序更好地控制其初始化方式。DatabaseProxy 对象是一个占位符,数据库可以在运行时使用它进行选择。

在以下示例中,将根据应用程序的配置设置选择合适的数据库。

from peewee import *
db_proxy = DatabaseProxy() # Create a proxy for our db.

class MyUser (Model):
   name=TextField()
   city=TextField(constraints=[SQL("DEFAULT 'Mumbai'")])
   age=IntegerField()
   class Meta:
      database=db_proxy
      db_table='MyUser'

# Based on configuration, use a different database.
if app.config['TESTING']:
   db = SqliteDatabase(':memory:')
elif app.config['DEBUG']:
   db = SqliteDatabase('mydatabase.db')
else:
   db = PostgresqlDatabase(
      'mydatabase', host='localhost', port=5432, user='postgres', password='postgres'
   )

# Configure our proxy to use the db we specified in config.
db_proxy.initialize(db)
db.connect()
db.create_tables([MyUser])

你还可以使用在数据库类和模型类中声明的 bind() 方法,在运行时将模型与任何数据库对象相关联。

以下示例在数据库类中使用 bind() 方法。

from peewee import *

class MyUser (Model):
   name=TextField()
   city=TextField(constraints=[SQL("DEFAULT 'Mumbai'")])
   age=IntegerField()

db = MySQLDatabase('mydatabase', host='localhost', port=3306, user='root', password='')
db.connect()
db.bind([MyUser])
db.create_tables([MyUser])

在 Model 类中也定义了 bind() 方法。

from peewee import *

class MyUser (Model):
   name=TextField()
   city=TextField(constraints=[SQL("DEFAULT 'Mumbai'")])
   age=IntegerField()

db = MySQLDatabase('mydatabase', host='localhost', port=3306, user='root', password='')
db.connect()
MyUser.bind(db)
db.create_tables([MyUser])

Peewee - Connection Management

默认情况下,数据库对象使用 autoconnect 参数设置为 True 创建。相反,为了以编程方式管理数据库连接,它最初被设置为 False。

db=SqliteDatabase("mydatabase", autoconnect=False)

数据库类具有 connect() 方法,用于建立与服务器上存在的数据库的连接。

db.connect()

强烈建议在完成操作后关闭连接。

db.close()

如果你试图打开一个已经打开的连接,Peewee 会引发 OperationError

>>> db.connect()
True
>>> db.connect()
Traceback (most recent call last):
   File "<stdin>", line 1, in <module>
   File "c:\peewee\lib\site-packages\peewee.py", line 3031, in connect
      raise OperationalError('Connection already opened.')
peewee.OperationalError: Connection already opened.

要避免此错误,可以使用 reuse_if_open=True 作为 connect() 方法的参数。

>>> db.connect(reuse_if_open=True)
False

在已经关闭的连接上调用 close() 不会导致错误。但是,你可以使用 is_closed() 方法检查连接是否已经关闭。

>>> if db.is_closed()==True:
   db.connect()

True
>>>

不必在最后显示调用 db.close(),你也可以将数据库对象用作 context_manager

from peewee import *

db = SqliteDatabase('mydatabase.db', autoconnect=False)

class User (Model):
   user_id=TextField(primary_key=True)
   name=TextField()
   age=IntegerField()
   class Meta:
      database=db
      db_table='User'
with db:
   db.connect()
   db.create_tables([User])

Peewee - Relationships and Joins

Peewee 支持实现不同类型的 SQL JOIN 查询。它的 Model 类有一个 join() 方法,它会返回一个 Join 实例。

M1.joint(m2, join_type, on)

join 表将 M1 模型映射到 m2 模型,并返回 Join 类实例。on 参数默认是 None,它是一个用作连接谓词的表达式。

Join Types

Peewee 支持以下 Join 类型(默认为 INNER)。

  1. JOIN.INNER

  2. JOIN.LEFT_OUTER

  3. JOIN.RIGHT_OUTER

  4. JOIN.FULL

  5. JOIN.FULL_OUTER

  6. JOIN.CROSS

为了演示 join() 方法的用法,首先声明以下模型:

db = SqliteDatabase('mydatabase.db')

class BaseModel(Model):
   class Meta:
      database = db

class Item(BaseModel):
   itemname = TextField()
      price = IntegerField()

class Brand(BaseModel):
   brandname = TextField()
      item = ForeignKeyField(Item, backref='brands')

class Bill(BaseModel):
   item = ForeignKeyField(Item, backref='bills')
   brand = ForeignKeyField(Brand, backref='bills')
   qty = DecimalField()

db.create_tables([Item, Brand, Bill])

Tables

接着,使用以下测试数据填充这些表:

Item Table

item 表如下:

item table

Brand Table

brand 表如下:

brand table

Bill Table

bill 表如下:

bill table

若要在 Brand 和 Item 表之间执行一个简单的连接操作,请执行以下代码:

qs=Brand.select().join(Item)
for q in qs:
print ("Brand ID:{} Item Name: {} Price: {}".format(q.id, q.brandname, q.item.price))

最终输出如下:

Brand ID:1 Item Name: Dell Price: 25000
Brand ID:2 Item Name: Epson Price: 12000
Brand ID:3 Item Name: HP Price: 25000
Brand ID:4 Item Name: iBall Price: 4000
Brand ID:5 Item Name: Sharp Price: 12000

Joining Multiple Tables

我们有一个 Bill 模型,它与 item 和 brand 模型有两个外键关系。若要从所有三个表中获取数据,请使用以下代码:

qs=Bill.select().join(Brand).join(Item)
for q in qs:
print ("BillNo:{} Brand:{} Item:{} price:{} Quantity:{}".format(q.id, \
q.brand.brandname, q.item.itemname, q.item.price, q.qty))

基于我们的测试数据,将显示以下输出:

BillNo:1 Brand:HP Item:Laptop price:25000 Quantity:5
BillNo:2 Brand:Epson Item:Printer price:12000 Quantity:2
BillNo:3 Brand:iBall Item:Router price:4000 Quantity:5

Peewee - Subqueries

在 SQL 中,子查询是另一个查询的 WHERE 子句中的嵌入式查询。我们可以将子查询实现为外层模型的 select() 语句的 where 属性中的一个 model.select() 作为参数。

为了演示 Peewee 中的子查询用法,让我们使用以下定义的模型:

from peewee import *
db = SqliteDatabase('mydatabase.db')

class BaseModel(Model):
   class Meta:
      database = db

class Contacts(BaseModel):
   RollNo = IntegerField()
   Name = TextField()
   City = TextField()

class Branches(BaseModel):
   RollNo = IntegerField()
   Faculty = TextField()

db.create_tables([Contacts, Branches])

在创建表后,用以下示例数据填充它们:

Contacts table

contacts 表如下:

data table
data table1

为了仅显示 ETC 教师注册过的 RollNo 的联系人和城市,以下代码会生成一个 SELECT 查询,其中 WHERE 子句中包含另一个 SELECT 查询。

#this query is used as subquery
faculty=Branches.select(Branches.RollNo).where(Branches.Faculty=="ETC")
names=Contacts.select().where (Contacts.RollNo .in_(faculty))

print ("RollNo and City for Faculty='ETC'")
for name in names:
   print ("RollNo:{} City:{}".format(name.RollNo, name.City))

db.close()

以上代码将显示以下结果:

RollNo and City for Faculty='ETC'
RollNo:103 City:Indore
RollNo:104 City:Nasik
RollNo:108 City:Delhi
RollNo:110 City:Nasik

Peewee - Sorting

可以使用 order_by 子句及其模型的 select() 方法从表中选择记录。此外,通过将 desc() 附加到要对其执行排序的字段属性上,可以按降序收集记录。

Example

以下代码按城市名称的升序显示联系表中的记录。

rows=Contacts.select().order_by(Contacts.City)
print ("Contact list in order of city")
for row in rows:
   print ("RollNo:{} Name: {} City:{}".format(row.RollNo,row.Name, row.City))

Output

以下是有序列表,按城市名称的升序排列。

Contact list in order of city
RollNo:107 Name: Beena City:Chennai
RollNo:102 Name: Amar City:Delhi
RollNo:108 Name: John City:Delhi
RollNo:103 Name: Raam City:Indore
RollNo:101 Name: Anil City:Mumbai
RollNo:106 Name: Hema City:Nagpur
RollNo:104 Name: Leena City:Nasik
RollNo:109 Name: Jaya City:Nasik
RollNo:110 Name: Raja City:Nasik
RollNo:105 Name: Keshav City:Pune

Example

以下代码按姓名字段的降序显示列表。

rows=Contacts.select().order_by(Contacts.Name.desc())
print ("Contact list in descending order of Name")
for row in rows:
   print ("RollNo:{} Name: {} City:{}".format(row.RollNo,row.Name, row.City))

Output

输出如下 −

Contact list in descending order of Name
RollNo:110 Name: Raja City:Nasik
RollNo:103 Name: Raam City:Indore
RollNo:104 Name: Leena City:Nasik
RollNo:105 Name: Keshav City:Pune
RollNo:108 Name: John City:Delhi
RollNo:109 Name: Jaya City:Nasik
RollNo:106 Name: Hema City:Nagpur
RollNo:107 Name: Beena City:Chennai
RollNo:101 Name: Anil City:Mumbai
RollNo:102 Name: Amar City:Delhi

Peewee - Counting and Aggregation

我们可以通过附加 count() 方法来查找任何 SELECT 查询中报告的记录数。例如,以下语句返回 Contacts 表中 City=’Nasik’ 的行数。

qry=Contacts.select().where (Contacts.City=='Nasik').count()
print (qry)

Example

SQL在 SELECT 查询中具有 GROUP BY 子句。Peewee 以 group_by() 方法的形式支持它。以下代码返回 Contacts 表中按城市统计的名称。

from peewee import *

db = SqliteDatabase('mydatabase.db')
class Contacts(BaseModel):
   RollNo = IntegerField()
   Name = TextField()
   City = TextField()
   class Meta:
      database = db

db.create_tables([Contacts])

qry=Contacts.select(Contacts.City, fn.Count(Contacts.City).alias('count')).group_by(Contacts.City)
print (qry.sql())
for q in qry:
   print (q.City, q.count)

Peewee 发出的 SELECT 查询将如下所示:

('SELECT "t1"."City", Count("t1"."City") AS "count" FROM "contacts" AS "t1" GROUP BY "t1"."City"', [])

Output

根据 Contacts 表中的样本数据,显示以下输出:

Chennai 1
Delhi   2
Indore  1
Mumbai  1
Nagpur  1
Nasik   3
Pune    1

Peewee - SQL Functions

美国国家标准协会 (ANSI) 结构化查询语言 (SQL) 标准定义了多种 SQL 函数。

以下之类的聚合函数在 Peewee 中很有用。

  1. AVG() - 返回平均值。

  2. COUNT() - 返回行数。

  3. FIRST() - 返回第一个值。

  4. LAST() - 返回最后一个值。

  5. MAX() - 返回最大值。

  6. MIN() - 返回最小值。

  7. SUM() - 返回总和。

为了实现这些 SQL 函数,Peewee 有一个 SQL 辅助函数 fn()。上面示例中,我们用它来查找每个城市的记录数。

以下示例构建了一个采用 SUM() 函数的 SELECT 查询。

使用前面定义的模型中的 Bill 和 Item 表,我们将显示如 Bill 表中输入的每一项的数量总和。

Item table

带有数据的 item 表如下所示 −

Id

Item Name

Price

1

Laptop

25000

2

Printer

12000

3

Router

4000

Bill table

bill 表如下:

Id

Item_id

Brand_id

Quantity

1

1

3

5

2

2

2

2

3

3

4

5

4

2

2

6

5

3

4

3

6

1

3

1

Example

我们创建 Bill 和 Item 表之间的连接,从 Item 表中选择项目名称,从 Bill 表中选择数量总和。

from peewee import *
db = SqliteDatabase('mydatabase.db')

class BaseModel(Model):
   class Meta:
      database = db

class Item(BaseModel):
   itemname = TextField()
   price = IntegerField()

class Brand(BaseModel):
   brandname = TextField()
   item = ForeignKeyField(Item, backref='brands')

class Bill(BaseModel):
   item = ForeignKeyField(Item, backref='bills')
   brand = ForeignKeyField(Brand,      backref='bills')
   qty = DecimalField()

db.create_tables([Item, Brand, Bill])

qs=Bill.select(Item.itemname, fn.SUM(Bill.qty).alias('Sum'))
   .join(Item).group_by(Item.itemname)
print (qs)
for q in qs:
   print ("Item: {} sum: {}".format(q.item.itemname, q.Sum))

db.close()

以上的脚本执行了以下 SELECT 查询 −

SELECT "t1"."itemname", SUM("t2"."qty") AS "Sum" FROM "bill" AS "t2"
INNER JOIN "item" AS "t1" ON ("t2"."item_id" = "t1"."id") GROUP BY "t1"."itemname"

Output

相应地,输出如下 −

Item: Laptop sum: 6
Item: Printer sum: 8
Item: Router sum: 8

Peewee - Retrieving Row Tuples/Dictionaries

无需创建模型实例就可以遍历 resultset。这可以通过使用以下方法实现 −

  1. tuples() method.

  2. dicts() method.

Example

以元组形式返回 SELECT 查询中的数据,请使用 tuples() 方法。

qry=Contacts.select(Contacts.City, fn.Count(Contacts.City).alias('count'))
   .group_by(Contacts.City).tuples()
lst=[]
for q in qry:
   lst.append(q)
print (lst)

Output

输出如下 −

[
   ('Chennai', 1),
   ('Delhi', 2),
   ('Indore', 1),
   ('Mumbai', 1),
   ('Nagpur', 1),
   ('Nasik', 3),
   ('Pune', 1)
]

Example

获取字典对象的集合 −

qs=Brand.select().join(Item).dicts()
lst=[]
for q in qs:
   lst.append(q)
print (lst)

Output

输出如下 −

[
   {'id': 1, 'brandname': 'Dell', 'item': 1},
   {'id': 2, 'brandname': 'Epson', 'item': 2},
   {'id': 3, 'brandname': 'HP', 'item': 1},
   {'id': 4, 'brandname': 'iBall', 'item': 3},
   {'id': 5, 'brandname': 'Sharp', 'item': 2}
]

Peewee - User defined Operators

Peewee 拥有 Expression 类,借助该类,我们可以向 Peewee 的运算符列表中添加任何自定义运算符。Expression 的构造函数需要三个参数,左操作数、运算符和右操作数。

op=Expression(left, operator, right)

使用 Expression 类,我们定义一个 mod() 函数,它接受 left 和 right 的参数,以及运算符 '%”。

from peewee import Expression # the building block for expressions

def mod(lhs, rhs):
   return Expression(lhs, '%', rhs)

Example

我们可以在 SELECT 查询中使用它来获取 Contacts 表中 id 为偶数的记录列表。

from peewee import *
db = SqliteDatabase('mydatabase.db')

class BaseModel(Model):
   class Meta:
      database = db

class Contacts(BaseModel):
   RollNo = IntegerField()
   Name = TextField()
   City = TextField()

db.create_tables([Contacts])

from peewee import Expression # the building block for expressions

def mod(lhs, rhs):
   return Expression(lhs,'%', rhs)
qry=Contacts.select().where (mod(Contacts.id,2)==0)
print (qry.sql())
for q in qry:
   print (q.id, q.Name, q.City)

此代码将发出由以下字符串表示的 SQL 查询 −

('SELECT "t1"."id", "t1"."RollNo", "t1"."Name", "t1"."City" FROM "contacts" AS "t1" WHERE (("t1"."id" % ?) = ?)', [2, 0])

Output

因此,输出如下 −

2  Amar Delhi
4  Leena Nasik
6  Hema Nagpur
8  John Delhi
10 Raja Nasik

Peewee - Atomic Transactions

Peewee 的 database 类拥有 atomic() 方法,它创建了一个上下文管理器。它开启一个新事务。在上下文块内部,可以根据事务是否成功完成或遇到异常来提交或回滚事务。

with db.atomic() as transaction:
   try:
      User.create(name='Amar', age=20)
      transaction.commit()
   except DatabaseError:
      transaction.rollback()

atomic() 也可作为装饰器使用。

@db.atomic()
def create_user(nm,n):
   return User.create(name=nm, age=n)

create_user('Amar', 20)

多个原子事务块也可以嵌套。

with db.atomic() as txn1:
   User.create('name'='Amar', age=20)

   with db.atomic() as txn2:
      User.get(name='Amar')

Peewee - Database Errors

Python 的 DB-API 标准(PEP 249 建议)指定了任何 DB-API 兼容模块(如 pymysql、pyscopg2 等)要定义的异常类类型。

Peewee API 为这些异常提供了易于使用的包装器。 PeeweeException 是 Peewee API 中定义了以下 Exception 类的基类 −

  1. DatabaseError

  2. DataError

  3. IntegrityError

  4. InterfaceError

  5. InternalError

  6. NotSupportedError

  7. OperationalError

  8. ProgrammingError

我们可以从 Peewee 实现上述异常,而不是尝试 DB-API 特定的异常。

Peewee - Query Builder

Peewee 还提供一个非 ORM API 来访问数据库。我们可以将数据库表和列绑定到 Peewee 中定义的 TableColumn 对象,而不是定义模型和字段,并使用它们来执行查询。

首先,声明一个与数据库中的表对应的 Table 对象。你必须指定表名和列的列表。还可以选择提供主键。

Contacts=Table('Contacts', ('id', 'RollNo', 'Name', 'City'))

这个表对象通过 bind() 方法与数据库绑定。

Contacts=Contacts.bind(db)

Example

现在,我们可以在这个表对象上使用 select() 方法设置一个 SELECT 查询,并迭代结果集,如下所示:

names=Contacts.select()
for name in names:
   print (name)

Output

默认情况下,行以字典的形式返回。

{'id': 1,  'RollNo': 101, 'Name': 'Anil', 'City': 'Mumbai'}
{'id': 2,  'RollNo': 102, 'Name': 'Amar', 'City': 'Delhi'}
{'id': 3,  'RollNo': 103, 'Name': 'Raam', 'City': 'Indore'}
{'id': 4,  'RollNo': 104, 'Name': 'Leena', 'City': 'Nasik'}
{'id': 5,  'RollNo': 105, 'Name': 'Keshav', 'City': 'Pune'}
{'id': 6,  'RollNo': 106, 'Name': 'Hema', 'City': 'Nagpur'}
{'id': 7,  'RollNo': 107, 'Name': 'Beena', 'City': 'Chennai'}
{'id': 8,  'RollNo': 108, 'Name': 'John', 'City': 'Delhi'}
{'id': 9,  'RollNo': 109, 'Name': 'Jaya', 'City': 'Nasik'}
{'id': 10, 'RollNo': 110, 'Name': 'Raja', 'City': 'Nasik'}

如果需要的话,它们可以作为元组、命名元组或对象获取。

Tuples

程序如下:

Example

names=Contacts.select().tuples()
for name in names:
   print (name)

Output

输出如下 −

(1, 101, 'Anil', 'Mumbai')
(2, 102, 'Amar', 'Delhi')
(3, 103, 'Raam', 'Indore')
(4, 104, 'Leena', 'Nasik')
(5, 105, 'Keshav', 'Pune')
(6, 106, 'Hema', 'Nagpur')
(7, 107, 'Beena', 'Chennai')
(8, 108, 'John', 'Delhi')
(9, 109, 'Jaya', 'Nasik')
(10, 110, 'Raja', 'Nasik')

Namedtuples

程序如下:

Example

names=Contacts.select().namedtuples()
for name in names:
   print (name)

Output

输出如下 −

Row(id=1, RollNo=101, Name='Anil', City='Mumbai')
Row(id=2, RollNo=102, Name='Amar', City='Delhi')
Row(id=3, RollNo=103, Name='Raam', City='Indore')
Row(id=4, RollNo=104, Name='Leena', City='Nasik')
Row(id=5, RollNo=105, Name='Keshav', City='Pune')
Row(id=6, RollNo=106, Name='Hema', City='Nagpur')
Row(id=7, RollNo=107, Name='Beena', City='Chennai')
Row(id=8, RollNo=108, Name='John', City='Delhi')
Row(id=9, RollNo=109, Name='Jaya', City='Nasik')
Row(id=10, RollNo=110, Name='Raja', City='Nasik')

要插入一条新记录,INSERT 查询的构建如下所示:

id = Contacts.insert(RollNo=111, Name='Abdul', City='Surat').execute()

如果要添加的记录列表存储为字典列表或元组列表,则可以批量添加它们。

Records=[{‘RollNo’:112, ‘Name’:’Ajay’, ‘City’:’Mysore’},
   {‘RollNo’:113, ‘Name’:’Majid’,’City’:’Delhi’}}

Or

Records=[(112, ‘Ajay’,’Mysore’), (113, ‘Majid’, ‘Delhi’)}

INSERT 查询的编写方式如下:

Contacts.insert(Records).execute()

Peewee Table 对象有 update() 方法来实现 SQL UPDATE 查询。要将 Nasik 中所有记录的 City 更改为 Nagar,我们使用以下查询。

Contacts.update(City='Nagar').where((Contacts.City=='Nasik')).execute()

最后,Peewee 中的 Table 类还有 delete() 方法来实现 SQL 中的 DELETE 查询。

Contacts.delete().where(Contacts.Name=='Abdul').execute()

Peewee - Integration with Web Frameworks

Peewee 可以与大多数 Python Web 框架 API 无缝协作。每当 Web 服务器网关接口 (WSGI) 服务器从客户端接收连接请求时,就会建立与数据库的连接,然后在响应传递之后关闭连接。

在基于 Flask 的 Web 应用程序中使用时,连接会对 @app.before_request 修饰符产生影响,并在 @app.teardown_request 上断开。

from flask import Flask
from peewee import *

db = SqliteDatabase('mydatabase.db')
app = Flask(__name__)

@app.before_request
def _db_connect():
   db.connect()

@app.teardown_request
def _db_close(exc):
   if not db.is_closed():
      db.close()

Peewee API 也可以在 Django. 中使用。为此,请在 Django 应用中添加一个中间件。

def PeeweeConnectionMiddleware(get_response):
   def middleware(request):
      db.connect()
      try:
         response = get_response(request)
      finally:
         if not db.is_closed():
            db.close()
      return response
   return middleware

中间件添加到 Django 的设置模块中。

# settings.py
MIDDLEWARE_CLASSES = (
   # Our custom middleware appears first in the list.
   'my_blog.middleware.PeeweeConnectionMiddleware',
   #followed by default middleware list.
   ..
)

Peewee 可以轻松地与 Bottle、Pyramid 和 Tornado 等其他框架一起使用。

Peewee - SQLite Extensions

Peewee 带有一个 Playhouse 名称空间。它是一个由各种扩展模块组成的集合。其中之一是 playhouse.sqlite_ext 模块。它主要定义 SqliteExtDatabase 类,该类继承 SqliteDatabase 类,支持以下附加功能:

Features of SQLite Extensions

Peewee 支持的 SQLite 扩展功能如下 −

  1. Full-text search.

  2. JavaScript 对象表示法 (JSON) 扩展集成。

  3. Closure table extension support.

  4. LSM1 extension support.

  5. User-defined table functions.

  6. 通过备份 API 备份至文件 backup_to_file() 支持在线备份。

  7. BLOB API 支持,用于存储二进制数据。

如果特别 JSONField 声明为某个字段属性,则可以存储 JSON 数据。

class MyModel(Model):
   json_data = JSONField(json_dumps=my_json_dumps)

要激活全文搜索,模型可以使用 DocIdField 来定义主键。

class NoteIndex(FTSModel):
   docid = DocIDField()
   content = SearchField()

   class Meta:
      database = db

FTSModel 是 VirtualModel 的子类,可在 http://docs.peewee-orm.com/en/latest/peewee/sqlite_ext.html#VirtualModel 获得,与 FTS3 和 FTS4 全文搜索扩展一起使用。Sqlite 将所有列类型都视为 TEXT(尽管你可以存储其他数据类型,Sqlite 会将它们视为文本)。

SearchField 是一个 Field 类,用于模型中表示全文搜索虚拟表的列。

SqliteDatabase 支持 AutoField 以增加主键。但是,SqliteExtDatabase 支持 AutoIncrementField 以确保主键始终呈单调递增,而不论是否删除行。

playhouse 命名空间 (playhouse.sqliteq) 中的 SqliteQ 模块定义了 SqliteExeDatabase 的一个子类,以处理对 SQLite 数据库的序列化并发写操作。

另一方面,playhouse.apsw 模块提供了对 apsw sqlite 驱动的支持。另一个 Python SQLite 封装器 (APSW) 速度快且可以处理嵌套事务,而这些事务是由你的代码显式管理的。

from apsw_ext import *
db = APSWDatabase('testdb')

class BaseModel(Model):
   class Meta:
      database = db

class MyModel(BaseModel):
   field1 = CharField()
   field2 = DateTimeField()

Peewee - PostgreSQL and MySQL Extensions

playhouse.postgres_ext 中定义的帮助程序启用了额外的 PostgreSQL 功能。该模块定义了 PostgresqlExtDatabase 类,并提供了以下额外字段类型,这些类型专门用于声明要映射到 PostgreSQL 数据库表的模型。

Features of PostgreSQL Extensions

Peewee 支持的 PostgreSQL 扩展功能如下 −

  1. ArrayField 字段类型,用于存储数组。

  2. HStoreField 字段类型,用于存储键/值对。

  3. IntervalField 字段类型,用于存储 timedelta 对象。

  4. JSONField 字段类型,用于存储 JSON 数据。

  5. BinaryJSONField 字段类型,用于存储 jsonb JSON 数据类型。

  6. TSVectorField 字段类型,用于存储全文搜索数据。

  7. DateTimeTZField 字段类型,可识别时区的日期时间字段。

此模块中的其他特定于 Postgres 的功能旨在提供。

  1. hstore support.

  2. server-side cursors.

  3. full-text search.

Postgres hstore 是一个键值存储,可嵌入到表中作为 HStoreField 类型字段之一。要启用 hstore 支持,请创建具有 register_hstore=True 参数的数据库实例。

db = PostgresqlExtDatabase('mydatabase', register_hstore=True)

使用一个 HStoreField 定义一个模型。

class Vehicles(BaseExtModel):
   type = CharField()
   features = HStoreField()

按如下方式创建一个模型实例 −

v=Vechicle.create(type='Car', specs:{'mfg':'Maruti', 'Fuel':'Petrol', 'model':'Alto'})

要访问 hstore 值 −

obj=Vehicle.get(Vehicle.id=v.id)
print (obj.features)

MySQL Extensions

playhouse.mysql_ext 模块中定义的 MySQLConnectorDatabase 提供 MysqlDatabase 类的备用实现。它使用 Python 的 DB-API 兼容官方 mysql/python connector

from playhouse.mysql_ext import MySQLConnectorDatabase

db = MySQLConnectorDatabase('mydatabase', host='localhost', user='root', password='')

Peewee - Using CockroachDB

CockroachDB 或蟑螂数据库 (CRDB) 是由计算机软件公司 Cockroach Labs 开发的。它是一个可扩展的、持续复制的事务性数据存储,旨在将数据副本存储在多个位置,以提供快速访问。

Peewee 通过 playhouse.cockroachdb 扩展模块中定义的 CockroachDatabase 类提供对该数据库的支持。该模块包含 CockroachDatabase 的定义,它是核心模块中 PostgresqlDatabase 类的子类。

此外,还有 run_transaction() 方法,它在事务内运行一个函数并提供自动客户端重试逻辑。

Field Classes

该扩展还具有某些特殊字段类,可用作 CRDB 兼容模型中的属性。

  1. UUIDKeyField - 一个主键字段,使用 CRDB 的 UUID 类型,并具有默认随机生成 UUID。

  2. RowIDField - 一个主键字段,使用 CRDB 的 INT 类型以及默认值 unique_rowid()。

  3. JSONField - 与 Postgres BinaryJSONField 相同。

  4. ArrayField - 与 Postgres 扩展相同,但不支持多维数组。