说明:peewee 中有很多方法是延时执行的,需要调用 execute() 方法使其执行。下文中不再特意说明这个问题,大家看代码。

本文中代码样例所使用的 Person 模型如下:

class Person(Model):

Name = CharField()

Age = IntegerField()

Birthday = DateTimeField()

Remarks = CharField(null=True)

复制代码

一、新增

1、create

Model.create 向数据库中插入一条记录,并返回一个新的实例。

p = Person.create(Name='张三', Age=30, Birthday=date(1990, 1, 1))

复制代码

2、save

语法:

save(force_insert=False, only=None)

复制代码

参数:

force_insert:是否强制插入

only(list):需要持久化的字段,当提供此参数时,只有提供的字段被持久化。

示例:

p1 = Person(Name='王五', Age=50, Birthday=date(1970, 1, 1))

p1.save()

复制代码

这里说的比较简单,下面会详细说明。

3、insert

insert 只插入数据而不创建模型实例,返回新行的主键。

Person.insert(Name='李四', Age=40, Birthday=date(1980, 1, 1)).execute()

复制代码

4、insert_many

语法:

insert_many(rows, fields=None)

复制代码

参数:

rows:元组或字典列表,要插入的数据

fields(list):需要插入的字段名列表。

说明:

1、当 rows 传递的是字典列表时,fields 是不需要传的,如果传了,那么,rows 中的字段在字典中必须存在,否则报错。如果没有传递 fields 参数,那么默认取所有字典的交集作为插入字段。这个也好理解,比如一个字典的键是a、b、c,一个是 b、c、d,那么就取 b、c 作为需要插入的字段。peewee 不会为缺失的字段做默认处理。

2、当 rows 传递的是元组列表时,必须指定 fields,并且 fields 中字段名的顺序跟元组一致。元组中值的数量必须大于等于 fields 中字段的数量,一般建议是保持一致。

示例:

Person.insert_many([

('张三', 30, date(1990, 1, 1)),

('李四', 40, date(1980, 1, 1)),

('王五', 50, date(1970, 1, 1))

],

['Name', 'Age', 'Birthday']

).execute()

Person.insert_many([

{'Name': '张三', 'Age': 30, 'Birthday': date(1990, 1, 1)},

{'Name': '李四', 'Age': 40, 'Birthday': date(1980, 1, 1)},

{'Name': '王五', 'Age': 50, 'Birthday': date(1970, 1, 1)}

]

).execute()

复制代码

对于批量操作,应该放在事务中执行:

with db.atomic():

Person.insert_many(data, fields=fields).execute()

复制代码在使用批量插入时,如果是 SQLite,SQLite3 版本必须为 3.7.11.0 或更高版本才能利用批量插入API。此外,默认情况下,SQLite 将 SQL 查询中的绑定变量数限制为 999。

SQLite 中,当批量插入的行数超过 999 时,就需要使用循环来将数据批量分组:

with db.atomic():

for idx in range(0, len(data), 100):

Person.insert_many(data[idx: idx+100], fields=fields).execute()

复制代码

Peewee 中带有一个分块辅助函数 chunked(),使用它可以有效地将通用迭代块分块为一系列批量迭代的迭代:

from peewee import chunked

# 一次插入 100 行.

with db.atomic():

for batch in chunked(data, 100):

Person.insert_many(batch).execute()

复制代码

5、bulk_create

语法:

bulk_create(model_list, batch_size=None)

复制代码

参数:

model_list (iterable):未保存的模型实例的列表或其他可迭代对象。

batch_size (int):每次批量插入的行数。如果未指定,则一次性全部插入。

示例:

简单来说,insert_many 使用字典或元组列表作为参数,而 model_list 使用模型实例列表作为参数,就这区别。

data = [Person(Name='张三~', Age=30, Birthday=date(1990, 1, 1)),

Person(Name='李四~', Age=40, Birthday=date(1980, 1, 1))]

with db.atomic():

Person.bulk_create(data)

复制代码**注意:**如果使用的是 Postgresql(支持该RETURNING子句),则先前未保存的模型实例将自动填充其新的主键值。

例如用的是 SQLite,执行上述代码之后,print(data[0].id) 显示的结果是 None。

6、batch_commit

这不是一个好的方法,来看下面的例子

data_dict = [{'Name': '张三', 'Age': 30, 'Birthday': date(1990, 1, 1)},

{'Name': '李四', 'Age': 40, 'Birthday': date(1980, 1, 1)},

{'Name': '王五', 'Age': 50, 'Birthday': date(1970, 1, 1)}]

for row in db.batch_commit(data_dict, 100):

p = Person.create(**row)

复制代码

查看 SQL 语句如下:

('BEGIN', None)

('INSERT INTO "person" ("Name", "Age", "Birthday") VALUES (?, ?, ?)', ['张三', 30, datetime.date(1990, 1, 1)])

('INSERT INTO "person" ("Name", "Age", "Birthday") VALUES (?, ?, ?)', ['李四', 40, datetime.date(1980, 1, 1)])

('INSERT INTO "person" ("Name", "Age", "Birthday") VALUES (?, ?, ?)', ['王五', 50, datetime.date(1970, 1, 1)])

复制代码

其实,batch_commit 就是自动添加了一个事务,然后一条条的插入,所以返回的模型实例中能获取到主键。

参数第一个是字典列表,第二个就是每多少条启用一个事务,大家可以把它改成 1 看下 SQL 语句就明白了。

7、insert_from

使用 SELECT 查询作为源 INSERT 数据。此 API 应用于 INSERT INTO ... SELECT FROM ... 形式的查询。

语法:

insert_from(query, fields)

复制代码

参数:

query:SELECT查询用作数据源

fields:要将数据插入的字段,此参数必须要的

**示例:**我们将 Person 表按原结构复制一个 Person2 表出来,以做演示。

data = Person.select(Person.Name, Person.Age, Person.Birthday)

Person2.insert_from(data, ['Name', 'Age', 'Birthday']).execute()

复制代码注意: 因为是 INSERT INTO ... SELECT FROM ... 形式的,所以数据源的列跟要插入的列必须保持一致。

二、删除

1、delete

delete 后加 where 删除指定记录,如果不加 where,则删除全部记录。

Person.delete().where(Person.Name=='王五').execute()

复制代码

2、delete_instance

删除给定的实例。

语法:

delete_instance(recursive=False, delete_nullable=False)

复制代码

示例:

p = Person.get(Person.Name=='张三')

p.delete_instance()

复制代码

delete_instance 直接执行删除了,不用调用execute() 方法。

参数:

一般我都是先讲参数再讲示例的,这次倒过来,示例其实很简单,一看就明白。但是这个参数缺需要好好讲下。

这两个参数都跟外键有关。我们修改一下测试用的模型。假设有这样两个模型,一个人员,一个部门,人员属于部门。

class Department(Model):

Name = CharField()

class Meta:

database = db

class Person(Model):

Name = CharField()

Age = IntegerField()

Birthday = DateTimeField()

Remarks = CharField(null=True)

Department = ForeignKeyField(Department, null=True) # 这里外键可为空和不可为空是不一样的,下面说明

class Meta:

database = db

复制代码

① 当 recursive=False 时,只删除了【部门】,【人员】没有影响,从 SQL 语句中可以看出。

d = Department.get(1)

d.delete_instance(recursive=False)

# 执行的 SQL 语句

('SELECT "t1"."id", "t1"."Name" FROM "department" AS "t1" WHERE ? LIMIT ? OFFSET ?', [1, 1, 0])

('DELETE FROM "department" WHERE ("department"."id" = ?)', [1])

复制代码

② 当 recursive=True ,并且外键不可为空时,会先删除【部门】下的【人员】,再删除【部门】。

d = Department.get(1)

d.delete_instance(recursive=True)

# 执行的 SQL 语句

('SELECT "t1"."id", "t1"."Name" FROM "department" AS "t1" WHERE ? LIMIT ? OFFSET ?', [1, 1, 0])

('DELETE FROM "person" WHERE ("person"."Department_id" = ?)', [1])

('DELETE FROM "department" WHERE ("department"."id" = ?)', [1])

复制代码

③ 当 recursive=True ,并且外键可为空时,先将【人员】的【部门ID(外键字段)】置为了 NULL,再删除【部门】。

d = Department.get(1)

d.delete_instance(recursive=True)

# 执行的 SQL 语句

('SELECT "t1"."id", "t1"."Name" FROM "department" AS "t1" WHERE ? LIMIT ? OFFSET ?', [1, 1, 0])

('UPDATE "person" SET "Department_id" = ? WHERE ("person"."Department_id" = ?)', [None, 1])

('DELETE FROM "department" WHERE ("department"."id" = ?)', [1])

复制代码

④ delete_nullable 仅在 recursive=True 且外键可为空时有效,和 ③ 一样,当 delete_nullable=True 时,会删除【人员】,而不是将【人员的部门ID】置为 NULL。

d = Department.get(1)

d.delete_instance(recursive=True, delete_nullable=True)

# 执行的 SQL 语句

('SELECT "t1"."id", "t1"."Name" FROM "department" AS "t1" WHERE ? LIMIT ? OFFSET ?', [1, 1, 0])

('DELETE FROM "person" WHERE ("person"."Department_id" = ?)', [1])

('DELETE FROM "department" WHERE ("department"."id" = ?)', [1])

复制代码

三、修改

1、save

之前说过,save() 方法可以插入一条记录,一旦模型实例具有主键,任何后续调用 save() 都将导致 UPDATE 而不是另一个 INSERT。模型的主键不会改变。

p = Person(Name='王五', Age=50, Birthday=date(1970, 1, 1))

p.save()

print(p1.id)

p.Remarks = 'abc'

p.save()

复制代码

这个例子,第一次执行的 save 是 INSERT,第二次是 UPDATE。

这里解释一下,Person 这个模型,我并没有指定主键,peewee 会自动增加一个名为 id 的自增列作为主键。在执行第一个 save() 方法的时候,主键没值,所以执行 INSERT,save() 方法执行之后,自增列的值就返回并赋给了模型实例,所以第二次调用 save() 执行的是 UPDATE。

如果模型中一开始就用 PrimaryKeyField 或 primary_key 指定了主键,那么 save 执行的永远都是 update,所以什么主键不存在则 INSERT,存在则 UPDATE 这种操作根本不存在,只能自己来写判断。

2、update

update 用于批量更新,方法相对简单,以下三种写法都可以

# 方法一

Person.update({Person.Name: '赵六', Person.Remarks: 'abc'}).where(Person.Name=='王五').execute()

# 方法二

Person.update({'Name': '赵六', 'Remarks': 'abc'}).where(Person.Name=='张三').execute()

# 方法三

Person.update(Name='赵六', Remarks='abc').where(Person.Name=='李四').execute()

复制代码

3、原子更新

看这样的一个需求,有一张表,记录博客的访问量,每次有人访问博客的时候,访问量+1。

因为懒得新建模型,我们就以 Person 模型的 Age + 1 来演示。

我们可以这样来写:

for p in Person.select():

p.Age += 1

p.save()

复制代码

这样当然是可以实现的,但是这不仅速度慢,而且如果多个进程同时更新计数器,它也容易受到竞争条件的影响。

我们可以用 update 方法来实现。

Person.update(Age=Person.Age+1).execute()

复制代码

四、查询

1、get

Model.get() 方法检索与给定查询匹配的单个实例。

语法:

get(*query, **filters)

复制代码

参数:

query:查询条件

filters:Mapping of field-name to value for Django-style filter. 我翻遍网上文章和官方文档都没找到这玩意怎么用!

示例:

p1 = Person.get(Name='张三')

复制代码

或者

p2 = Person.get(Person.Name == '李四')

复制代码

当获取的结果不存在时,报 Model.DoesNotExist 异常。如果有多条记录满足条件,则返回第一条。

2、get_or_none

如果当获取的结果不存在时,不想报错,可以使用 Model.get_or_none() 方法,会返回 None,参数和 get 方法一致。

3、get_by_id

对于主键查找,还可以使用快捷方法Model.get_by_id()。

Person.get_by_id(1)

复制代码

4、get_or_create

Peewee 有一个辅助方法来执行“获取/创建”类型的操作: Model.get_or_create() 首先尝试检索匹配的行。如果失败,将创建一个新行。

p, created = Person.get_or_create(Name='赵六', defaults={'Age': 80, 'Birthday': date(1940, 1, 1)})

print(p, created)

# SQL 语句

('SELECT "t1"."id", "t1"."Name", "t1"."Age", "t1"."Birthday", "t1"."Remarks" FROM "person" AS "t1" WHERE ("t1"."Name" = ?) LIMIT ? OFFSET ?', ['赵六', 1, 0])

('BEGIN', None)

('INSERT INTO "person" ("Name", "Age", "Birthday") VALUES (?, ?, ?)', ['赵六', 80, datetime.date(1940, 1, 1)])

复制代码

参数:

get_or_create 的参数是 **kwargs,其中 defaults 为非查询条件的参数,剩余的为尝试检索匹配的条件,这个看执行时的 SQL 语句就一目了然了。对于“创建或获取”类型逻辑,通常会依赖唯一 约束或主键来防止创建重复对象。但这并不是强制的,比如例子中,我以 Name 为条件,而 Name 并非主键。只是最好不要这样做。

返回值:

get_or_create 方法有两个返回值,第一个是“获取/创建”的模型实例,第二个是是否新创建。

5、select

使用 Model.select() 查询获取多条数据。select 后可以添加 where 条件,如果不加则查询整个表。

语法:

select(*fields)

复制代码

参数:

fields:需要查询的字段,不传时返回所有字段。传递方式如下例所示。

示例:

ps = Person.select(Person.Name, Person.Age).where(Person.Name == '张三')

复制代码

select() 返回结果是一个 ModelSelect 对象,该对象可迭代、索引、切片。当查询不到结果时,不报错,返回 None。并且 select() 结果是延时返回的。如果想立即执行,可以调用 execute() 方法。

注意:where 中的条件不支持 Name='张三' 这种写法,只能是 Person.Name == '张三'。

6、获取记录条数 count 方法

使用 .count() 方法可以获取记录条数。

Person.select().count()

复制代码

也许你会问,用 len() 方法可以吗?当然也是可以的,但是是一种不可取的方法。

len(Person.select())

复制代码

这两者的实现方式天差地远。用 count() 方法,执行的 SQL 语句是:

('SELECT COUNT(1) FROM (SELECT 1 FROM "person" AS "t1") AS "_wrapped"', [])

复制代码

而用 len() 方法执行的 SQL 语句却是:

('SELECT "t1"."id", "t1"."Name", "t1"."Age", "t1"."Birthday", "t1"."Remarks" FROM "person" AS "t1"', [])

复制代码

直接返回所有记录然后获取长度,这种方法是非常不可取的。

7、排序 order_by 方法

Person.select().order_by(Person.Age)

复制代码

排序默认是升序排列,也可以用 + 或 asc() 来明确表示是升序排列:

Person.select().order_by(+Person.Age)

Person.select().order_by(Person.Age.asc())

复制代码

用 - 或 desc() 来表示降序:

Person.select().order_by(-Person.Age)

Person.select().order_by(Person.Age.desc())

复制代码

如要对多个字段进行排序,逗号分隔写就可以了。

五、查询条件

当查询条件不止一个,需要使用逻辑运算符连接,而 Python 中的 and、or 在 Peewee 中是不支持的,此时我们需要使用 Peewee 封装好的运算符,如下:

逻辑符

含义

样例

&

and

Person.select().where((Person.Name == '张三') & (Person.Age == 30))

|

or

Person.select().where((Person.Name == '张三') \| (Person.Age == 30))

~

not

Person.select().where(~Person.Name == '张三')

特别注意:有多个条件时,每个条件必须用 () 括起来。

当条件全为 and 时,也可以用逗号分隔,get 和 select 中都可以:

Person.get(Person.Name == '张三', Person.Age == 30)

复制代码

六、支持的比较符

运算符

含义

==

等于

<

小于

<=

小于等于

>

大于

>=

大于等于

!=

不等于

<<

x in y,其中 y 是列表或查询

>>

x is y, 其中 y 可以是 None

%

x like y

**

x like y

注意:由于 SQLite 的 LIKE 操作默认情况下不区分大小写,因此 peewee 将使用 SQLite GLOB 操作进行区分大小写的搜索。glob 操作使用星号表示通配符,而不是通常的百分号。如果您正在使用 SQLite 并希望区分大小写的部分字符串匹配,请记住使用星号作为通配符。

解释一下,在 SQLite 中,如果希望 like 的时候区分大小写,可以这么写:

Person.select().where(Person.Remarks % 'a*')

复制代码

如果不希望区分大小写,这么写:

Person.select().where(Person.Remarks ** 'a%')

复制代码

python 数据库框架peewee_Python:轻量级 ORM 框架 peewee 用法详解之——增删改查相关推荐

  1. 【Python】列表、元组、字典的使用详解(增删改查)

    目录 一.列表 1)列表的概念 2)列表的创建 3)列表的访问 4)列表添加元素 5)列表删除元素 6)列表修改元素 7)列表的 * 和 + 操作 二.元组 1)元组的概念 2)元组的创建 3)元组的 ...

  2. Spring+SpringMVC+MyBatis明日方舟版人员信息管理系统前端页面代码前后端交互+SSM框架 管理员登录 游客登录 普通用户登录 人员的增删改查 信息更新 图片上传 分页查询)

    Spring+SpringMVC+MyBatis明日方舟版人员信息管理系统前端页面代码(前后端交互+SSM框架 管理员登录 游客登录 普通用户登录 人员的增删改查 信息更新 图片上传 分页查询 修改密 ...

  3. Python函数(函数定义、函数调用)用法详解

    函数 函数就是一段封装好的,可以重复使用的代码,它使得我们的程序更加模块化,不需要编写大量重复的代码. 函数可以提前保存起来,并给它起一个独一无二的名字,只要知道它的名字就能使用这段代码.函数还可以接 ...

  4. Python中第三方库Requests库的高级用法详解

    Python中第三方库Requests库的高级用法详解 虽然Python的标准库中urllib2模块已经包含了平常我们使用的大多数功能,但是它的API使用起来让人实在感觉不好.它已经不适合现在的时代, ...

  5. 电信报表java_china_netcom 用java和框架Strus开发的电信报表系统,信息 的增删改查 Develop 238万源代码下载- www.pudn.com...

    文件名称: china_netcom下载 收藏√  [ 5  4  3  2  1 ] 开发工具: Java 文件大小: 532 KB 上传时间: 2014-09-04 下载次数: 1 提 供 者: ...

  6. Mysql —— C语言链接mysql数据库,命令行形式(getopt()函数),用户、用户组增删改查(用户组表内有用户控制的策略字段)

    函数说明--getopt(): 函数说明 getopt()用来分析命令行参数.参数argc和argv分别代表参数个数和内容,跟main()函数的命令行参数是一样的. optstring中的指定的内容的 ...

  7. python 函数参数self_Python类中self参数用法详解

    Python编写类的时候,每个函数参数第一个参数都是self,一开始我不管它到底是干嘛的,只知道必须要写上.后来对Python渐渐熟悉了一点,再回头看self的概念,似乎有点弄明白了. 首先明确的是s ...

  8. python的argv是什么意思_Python argv用法详解

    想用python处理一下文件,发现有argv这个用法,搜来学习一下. 如果想对python脚步传参数,那么就需要命令行参数的支持了,这样可以省的每次去改脚步了. 用法是:python xx.py xx ...

  9. Java-spring数据库编程(idea)实现学生账号登录以及管理员增删改查功能

    通过所学的Spring数据库编程知识,实现学生管理系统的登录及增删改查的功能.要求学生在控制台输入用户名密码,如果用户账号密码正确则显示登录成功,如果登录失败则显示登录失败.登录成功后,可以进行增删改 ...

  10. python中count的作用_python count函数用法详解

    在python中可以使用"count()"函数统计字符串里某个字符出现的次数,该函数用于统计次数,其语法是"count(sub, start= 0,end=len(str ...

最新文章

  1. 每个程序员都必须知道的8种数据结构
  2. 关于Integer类中parseInt()和valueOf()方法的区别以及int和String类性的转换.以及String类valueOf()方法...
  3. 4.编程打印一个二维数组中所有元素的和,并打印最大值,最小值(以及它们所在的行号和列号)
  4. 微软将推出桌面虚拟化软件
  5. android webdav客户端,WebDAV精灵(WebDAV客户端)
  6. python 处理python编码的基本过程
  7. 鲜为人知的6个黑科技网站_6种鲜为人知的熊猫绘图工具
  8. 遇到oracle错误1012,跟着感觉走,解决安装RAC过程中OCR完整性错误,待深入剖析...
  9. mysql 执行计划不对_mysql tokudb执行计划走的不准确案例
  10. oracle设置默认值为当前时间_把锁屏密码设置成当前时间,随时间永远变动!
  11. 机器学习概念笔记(1)——混淆矩阵、Precision、Recall、F-score
  12. linux系统权限和用户
  13. scipy安装_4. Python--Scipy库(下/13-17)
  14. vue综合实例——音乐播放器(悦听player)
  15. 系统性谈谈软件可靠性——第3讲:软件可靠性设计方法
  16. 微型计算机软件系统分为什么,微型计算机软件微型计算机软件主要包括哪些软件?...
  17. Build: 0 succeeded, 0 failed, 1 up-to-date, 0 skipped
  18. 奇怪的同床人:指纹现象……或state.gov与facebook.com
  19. IA-32:Privilege level
  20. nodejs todu小damo

热门文章

  1. ROS下同时接收多个话题并实现相机和雷达的数据融合
  2. day10.函数,函数的参数
  3. 如果需要一个图形学算法
  4. Tomcat----windows系统通过命令符“强制关闭Tomcat”
  5. C++对象池技术剖析
  6. 才知道系列之GroupOn
  7. 汇编程序16位带符号变量计算
  8. 智能优化算法:金枪鱼群优化算法-附代码
  9. leetcode刷题日记-1995. 统计特殊四元组
  10. 模板题——图论相关(2)