Python数据库ORM SQLAlchemy 0.7学习笔记(4) 添加对象

2024-06-02 11:32

本文主要是介绍Python数据库ORM SQLAlchemy 0.7学习笔记(4) 添加对象,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1. 添加一个新对象

前面介绍了映射到实体表的映射类User,如果我们想将其持久化(Persist),那么就需要将这个由User类建立的对象实例添加到我们先前创建的Session会话实例中:

ed_user = User('ed', 'Ed Jones', 'edspassword')
session.add(ed_user)

上面两段代码执行完后对象持久化了么?你或许会兴冲冲的跑去数据库里查看,结果却失望而归——数据库里什么都没有。为什么呢?因为SQLAlchemy采取的是Lazyload策略,也就是说现在这个对象被标记为Pending准备状态,但没有执行任何可能导致数据库变化的SQL语句。那么什么时候会执行SQL语句并真正持久化呢?这个要等SQLAlchemy觉得需要的时候,比如我们现在查询这个对象、对象的一个属性或者显式的调用flush方法,这时候SQLAlchemy觉得它“是时候”或者“不得不”执行SQL数据库查询以便于把标记为Pending的数据写入数据库表中了。假如这时候你执行的获取对象、对象属性或者类似的操作,SQLAlchemy在执行完SQL语句后会将你所要查询的数据反馈给你。

为了更好的说明这一点,这里举一个例子,这里涉及到我们第一个查询示例,我们调用了Query对象来帮助我们完成这些,比如这里我们获取刚刚持久化的用户ed,我们通过“过滤(filter by)”的方式来查询用户名为ed的用户,当然我们只需要一个ed,假如有多个重名的ed的话,查询将会返回所有叫ed的记录集列表,我们就选择第一个ed吧(first)。

>>> our_user = session.query(User).filter_by(name='ed').first() 
BEGIN (implicit)
INSERT INTO users (name, fullname, password) VALUES (?, ?, ?)
('ed', 'Ed Jones', 'edspassword')
SELECT users.id AS users_id,users.name AS users_name,users.fullname AS users_fullname,users.password AS users_password
FROM users
WHERE users.name = ?LIMIT ? OFFSET ?
('ed', 1, 0)
>>> our_user
<User('ed','Ed Jones', 'edspassword')>

可以看到上面的查询语句返回了一个User的实例,而这个实例恰恰是我们先前持久化的。同时由于我们指定了引擎的echo=True,所以再执行查询时输出了SQL语句,我们注意到除了普通的SELECT外,还有额外的INSERT语句,而INSERT处理的就是我们刚刚通过session.add()持久化标记为Pending的对象,也就是说你在实际操作持久化数据时才会由延迟加载(lazyload)真正触发数据库操作。

实际上Session查询反馈给我们的User对象和我们刚刚持久化的对象是同一个对象,通过下面的代码可以检验:

>>> ed_user is our_user
True

实际上这里ORM的操作概念有点类似于标识映射(identity map),也就是说在实体数据库之前架设一张标识映射表,可以看作缓存表的一种,任何存储数据库的对象会事先停留在这张表上,如果我们要查询一个对象,将事先查询这张标识映射表,如果这个对象存在则直接取出,否则就会查询实体数据库,我觉得这个有点像缓存的作用,可以这么理解吧。

identity map

一旦一个带有独一无二的主键的对象被Session持久化了,所有使用该主键在同一Session上查询的对象将是同一个Python对象。当然对于在这个会话中持久化另外一个具有相同主键的对象将会抛出异常错误(主键不能重复)。

如果我们想一次性添加多个对象到Session中可以调用add_all()

>>> session.add_all([
...     User('wendy', 'Wendy Williams', 'foobar'),
...     User('mary', 'Mary Contrary', 'xxg527'),
...     User('fred', 'Fred Flinstone', 'blah')])

下面再谈谈修改,假如Ed觉得他的密码不太安全,决定修改,可以直接这么做:

>>> ed_user.password = 'f8s7ccs'

同样的道理,这个改动不会立即反映到数据库里,当然Session意识到你要修改Ed的密码,它会暂时缓冲这个改动,我们可以通过dirty方法了解到我们的改动:

>>> session.dirty
IdentitySet([<User('ed','Ed Jones', 'f8s7ccs')>])

同样的我们可以通过new方法“窥看”到先前用add_all()持久化的对象列表:

>>> session.new  
IdentitySet([<User('wendy','Wendy Williams', 'foobar')>,
<User('mary','Mary Contrary', 'xxg527')>,
<User('fred','Fred Flinstone', 'blah')>])

当然这些改动都没有真正反馈到数据库里,相当于都被ORM缓冲了。接下来我们可以显式的调用commit()来告诉Session:“我们目前就添加或者改动这么多可以提交数据库了”:

>>> session.commit()
UPDATE users SET password=? WHERE users.id = ?
('f8s7ccs', 1)
INSERT INTO users (name, fullname, password) VALUES (?, ?, ?)
('wendy', 'Wendy Williams', 'foobar')
INSERT INTO users (name, fullname, password) VALUES (?, ?, ?)
('mary', 'Mary Contrary', 'xxg527')
INSERT INTO users (name, fullname, password) VALUES (?, ?, ?)
('fred', 'Fred Flinstone', 'blah')
COMMIT

于是刚才缓冲的数据或者变更全部被作为事务一次性flush到数据库了,通过输出的SQL语句我们也可以看出来。

这个操作完成后被会话(Session)引用的数据库连接资源将被回收到连接池中,接下来的对于这个Session的任何操作将会触发一个新的事务(Transaction),当然会再次和连接池申请获得数据库连接资源。

之前文章介绍到Ed的User对象的idNone,现在让我们来看看吧:

>>> ed_user.id 
BEGIN (implicit)
SELECT users.id AS users_id,users.name AS users_name,users.fullname AS users_fullname,users.password AS users_password
FROM users
WHERE users.id = ?
(1,)
1

除了由于echo=True导致输出的SQL语句,看看是不是有了值,值为1。

无论是立即(commitflush)或者通过“首次访问加载(load-on-first-access)”,在Session在数据库插入一条新记录后,所有新生成的标识和数据库生成的默认值对于实例来说才可以被访问到。

当调用了commit()以后,SQLAlchemy将会刷新当前事务的所有数据到数据库里。

2. 事务回滚

本文以及同系列的文章是以自己的想法翻译的,不当之处还请指正,不做权威依据。好了,下面我还是简单介绍一下事务回滚吧,其实这个和数据库的事务回滚一个意思,就是我们做错事后要撤消之前的变更。

因为Session是作为事务(transaction)来工作的,所以我们可以回滚(roll back)先前所做的更改。接下来让我们做两个稍后会被撤销(回滚)的更改,第一个是修改ed_user.name

>>> ed_user.name = 'Edwardo'

第二个是增加一个“不期望”的用户fake_user

>>> fake_user = User('fakeuser', 'Invalid', '12345')
>>> session.add(fake_user)

查询当前会话,我们可以看到这两个变更已经被flush到当前事务里了:

>>> session.query(User).filter(User.name.in_(['Edwardo', 'fakeuser'])).all() 
UPDATE users SET name=? WHERE users.id = ?
('Edwardo', 1)
INSERT INTO users (name, fullname, password) VALUES (?, ?, ?)
('fakeuser', 'Invalid', '12345')
SELECT users.id AS users_id,users.name AS users_name,users.fullname AS users_fullname,users.password AS users_password
FROM users
WHERE users.name IN (?, ?)
('Edwardo', 'fakeuser')
[<User('Edwardo','Ed Jones', 'f8s7ccs')>, <User('fakeuser','Invalid', '12345')>]

好吧,接下来是见证奇迹的时刻,我们回滚(rolling back)事务:

>>> session.rollback()
ROLLBACK
>>> ed_user.name 
BEGIN (implicit)
SELECT users.id AS users_id,users.name AS users_name,users.fullname AS users_fullname,users.password AS users_password
FROM users
WHERE users.id = ?
(1,)
u'ed'
>>> fake_user in session
False

我们可以看到ed_user的名字变回ed,并且我们不期望的用户fake_user被“踢出”会话(Session)了。

最后,我们可以查询一下用户名在['ed', 'fakeuser']范围的用户,确保我们的更改是有效的:

>>> session.query(User).filter(User.name.in_(['ed', 'fakeuser'])).all()
SELECT users.id AS users_id,users.name AS users_name,users.fullname AS users_fullname,users.password AS users_password
FROM users
WHERE users.name IN (?, ?)
('ed', 'fakeuser')
[<User('ed','Ed Jones', 'f8s7ccs')>]

好了,今天就到这里,今天我们讲解了添加对象和事务回滚,或多或少穿插了些简单的查询,接下来我们会介绍较为复杂一些的查询语句,敬请期待!

这篇关于Python数据库ORM SQLAlchemy 0.7学习笔记(4) 添加对象的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1023846

相关文章

C++对象布局及多态实现探索之内存布局(整理的很多链接)

本文通过观察对象的内存布局,跟踪函数调用的汇编代码。分析了C++对象内存的布局情况,虚函数的执行方式,以及虚继承,等等 文章链接:http://dev.yesky.com/254/2191254.shtml      论C/C++函数间动态内存的传递 (2005-07-30)   当你涉及到C/C++的核心编程的时候,你会无止境地与内存管理打交道。 文章链接:http://dev.yesky

51单片机学习记录———定时器

文章目录 前言一、定时器介绍二、STC89C52定时器资源三、定时器框图四、定时器模式五、定时器相关寄存器六、定时器练习 前言 一个学习嵌入式的小白~ 有问题评论区或私信指出~ 提示:以下是本篇文章正文内容,下面案例可供参考 一、定时器介绍 定时器介绍:51单片机的定时器属于单片机的内部资源,其电路的连接和运转均在单片机内部完成。 定时器作用: 1.用于计数系统,可

问题:第一次世界大战的起止时间是 #其他#学习方法#微信

问题:第一次世界大战的起止时间是 A.1913 ~1918 年 B.1913 ~1918 年 C.1914 ~1918 年 D.1914 ~1919 年 参考答案如图所示

[word] word设置上标快捷键 #学习方法#其他#媒体

word设置上标快捷键 办公中,少不了使用word,这个是大家必备的软件,今天给大家分享word设置上标快捷键,希望在办公中能帮到您! 1、添加上标 在录入一些公式,或者是化学产品时,需要添加上标内容,按下快捷键Ctrl+shift++就能将需要的内容设置为上标符号。 word设置上标快捷键的方法就是以上内容了,需要的小伙伴都可以试一试呢!

Tolua使用笔记(上)

目录   1.准备工作 2.运行例子 01.HelloWorld:在C#中,创建和销毁Lua虚拟机 和 简单调用。 02.ScriptsFromFile:在C#中,对一个lua文件的执行调用 03.CallLuaFunction:在C#中,对lua函数的操作 04.AccessingLuaVariables:在C#中,对lua变量的操作 05.LuaCoroutine:在Lua中,

AssetBundle学习笔记

AssetBundle是unity自定义的资源格式,通过调用引擎的资源打包接口对资源进行打包成.assetbundle格式的资源包。本文介绍了AssetBundle的生成,使用,加载,卸载以及Unity资源更新的一个基本步骤。 目录 1.定义: 2.AssetBundle的生成: 1)设置AssetBundle包的属性——通过编辑器界面 补充:分组策略 2)调用引擎接口API

Javascript高级程序设计(第四版)--学习记录之变量、内存

原始值与引用值 原始值:简单的数据即基础数据类型,按值访问。 引用值:由多个值构成的对象即复杂数据类型,按引用访问。 动态属性 对于引用值而言,可以随时添加、修改和删除其属性和方法。 let person = new Object();person.name = 'Jason';person.age = 42;console.log(person.name,person.age);//'J

大学湖北中医药大学法医学试题及答案,分享几个实用搜题和学习工具 #微信#学习方法#职场发展

今天分享拥有拍照搜题、文字搜题、语音搜题、多重搜题等搜题模式,可以快速查找问题解析,加深对题目答案的理解。 1.快练题 这是一个网站 找题的网站海量题库,在线搜题,快速刷题~为您提供百万优质题库,直接搜索题库名称,支持多种刷题模式:顺序练习、语音听题、本地搜题、顺序阅读、模拟考试、组卷考试、赶快下载吧! 2.彩虹搜题 这是个老公众号了 支持手写输入,截图搜题,详细步骤,解题必备

《offer来了》第二章学习笔记

1.集合 Java四种集合:List、Queue、Set和Map 1.1.List:可重复 有序的Collection ArrayList: 基于数组实现,增删慢,查询快,线程不安全 Vector: 基于数组实现,增删慢,查询快,线程安全 LinkedList: 基于双向链实现,增删快,查询慢,线程不安全 1.2.Queue:队列 ArrayBlockingQueue:

Python 字符串占位

在Python中,可以使用字符串的格式化方法来实现字符串的占位。常见的方法有百分号操作符 % 以及 str.format() 方法 百分号操作符 % name = "张三"age = 20message = "我叫%s,今年%d岁。" % (name, age)print(message) # 我叫张三,今年20岁。 str.format() 方法 name = "张三"age