Create an Instance of the Mapped Class
映射完成后,我们可以创建一个User
对象:
>>> ed_user = User(name='ed', fullname='Ed Jones', nickname='edsnickname')
>>> ed_user.name
'ed'
>>> ed_user.nickname
'edsnickname'
>>> str(ed_user.id)
'None'
上一节构建的User
类已经提供了一个构造函数(例如__init__()
方法),它自动接受与我们所映射的列相匹配的关键字名称。我们可以自由地在我们的类上定义任何我们喜欢的显式__init__()
方法,它将覆盖Declarative提供的默认方法。
即使我们没有在构造函数中指定id
,当我们访问id
属性时,它仍然会产生一个None
的值。
Creating a Session
我们现在准备开始与数据库对话。ORM对数据库的 "句柄 "是Session
。当我们第一次设置应用程序时,在我们的create_engine()
语句的同一层次,我们定义了一个Session
类,它将作为新Session
对象的工厂。
>>> from sqlalchemy.orm import sessionmaker
>>> Session = sessionmaker(bind=engine)
什么是“工厂”?比如说多个模型类进行修改的操作,一旦有一个出现异常,那么要集体回滚,这个所谓的集体(就是这多个模型类)他们在这一次保存操作中,必须关联起来,才能做到要成功一起成,要滚一起滚,会话,就是一个把他们建立联系的那个工厂。
Session只是对象的一个工作空间,它是一个特定数据库连接的本地空间–如果你把一个应用程序线程看成是晚宴上的客人,那么Session就是客人的盘子,它所容纳的对象就是盘中的食物。
在你定义模块级对象时,如果你的应用程序还没有Engine
,就这样设置。
>>> Session = sessionmaker()
之后,当你使用create_engine()
创建引擎时,使用sessionmaker.configure()
将其连接到Session
。
>>> Session.configure(bind=engine) # once engine is available
这个定制的Session
类将创建新的Session
对象,这些对象绑定到我们的数据库。在调用sessionmaker
时,也可以定义其他的事务特性;这些将在后面的章节中描述。然后,每当需要与数据库进行对话时,就会实例化一个Session
。
>>> session = Session()
具体而言,两种方法如下
# 1. 导入sessionmaker(用于创建Session)
from sqlalchemy.orm import sessionmaker
# 2. 两种创建Session的方式,只是使用engine的时候不同
# 2.1 第一种方式
Session = sessionmaker(bind=engine) # 注意,Session是一个为实例化的Class对象
session = Session() # 实例化session
# 2.2 第二种方式
Session = sessionmaker() # 先创建一个为实例化的Session类对象
session = Session(bind=engine) # 实例化的时候再加入engine
Adding and Updating Objects
为了更新我们的User
对象,我们使用Session.add()
把它放到我们的Session
中。
>>> ed_user = User(name='ed', fullname='Ed Jones', nickname='edsnickname')
>>> session.add(ed_user)
此时,我们说实例是待定的;还没有发出SQL,对象还没有在数据库中用行表示。Session
会在需要时尽快发出SQL来持久化Ed Jones
,这个过程称为flush。如果我们在数据库中查询Ed Jones
,所有待处理的信息将首先被刷新,之后立即发出查询。
例如,下面我们创建一个新的Query
对象,它加载User
的实例。我们通过ed
的name
属性进行 “过滤”,并表示我们只希望在完整的行列表中得到第一个结果。返回一个User
实例,该实例与我们添加的实例相当。
SQL>>> our_user = session.query(User).filter_by(name='ed').first()
>>> our_user
<User(name='ed', fullname='Ed Jones', nickname='edsnickname')>
事实上,"会话 "已经识别出返回的行与已经在其内部对象映射中表示的行是相同的行,所以我们实际上得到的是与我们刚才添加的相同的实例。
>>> ed_user is our_user
True
这里工作的ORM概念被称为身份映射,它确保对 "会话 "中某一特定行的所有操作都是对同一数据集进行的。一旦一个具有特定主键的对象存在于Session
中,所有对Session
的SQL查询将总是返回该特定主键的相同Python对象;如果试图在会话中放置第二个具有相同主键的已经存在的对象,它也将引发一个错误。
我们可以使用add_all()
一次性添加更多的User
对象。
>>> session.add_all([
... User(name='wendy', fullname='Wendy Williams', nickname='windy'),
... User(name='mary', fullname='Mary Contrary', nickname='mary'),
... User(name='fred', fullname='Fred Flintstone', nickname='freddy')])
修改Ed的nickname:
>>> ed_user.nickname = 'eddie'
Session
知道 Ed Jones
被修改了:
>>> session.dirty
IdentitySet([<User(name='ed', fullname='Ed Jones', nickname='eddie')>])
并有三个新的 "用户 "对象正在等待。
>>> session.new
IdentitySet([<User(name='wendy', fullname='Wendy Williams', nickname='windy')>,
<User(name='mary', fullname='Mary Contrary', nickname='mary')>,
<User(name='fred', fullname='Fred Flintstone', nickname='freddy')>])
我们告诉Session
,我们要向数据库发出所有剩余的更改,并提交整个过程中的事务。我们通过Session.commit()
来完成这项工作。Session
为 "ed "上的昵称变化发出UPDATE
语句,以及为我们新增的三个User
对象发出INSERT
语句。
SQL>>> session.commit()
Session.commit()
将剩余的更改刷新到数据库中,并提交事务。会话引用的连接资源现在被返回到连接池中。随后对这个会话的操作将在一个新的事务中进行,它将在第一次需要时再次重新获取连接资源。
如果我们看Ed的id
属性,之前是None
,现在有了一个值。
SQL>>> ed_user.id
1
在Session
在数据库中插入新行后,所有新生成的标识符和数据库生成的默认值都会在实例上变得可用,要么立即可用,要么通过先访问后加载。在这种情况下,由于在我们发出Session.commit()
之后开始了一个新的事务,所以整个行在访问时被重新加载。SQLAlchemy默认情况下,在新事务中第一次访问前一个事务的数据时,会刷新数据,因此可以使用最新的状态。重载的级别是可以配置的,如使用会话中所述。
总结
# 创建实例对象
ed_user = User(name='ed', fullname='Ed Jones', nickname='edsnickname')
# 创建会话
# 1. 导入sessionmaker(用于创建Session)
from sqlalchemy.orm import sessionmaker
# 2. 两种创建Session的方式,只是使用engine的时候不同
# 2.1 第一种方式
Session = sessionmaker(bind=engine)
session = Session()
# 2.2 第二种方式
Session = sessionmaker()
session = Session(bind=engine)
# 将实例对象加入会话
# 加入一个
session.add(ed_user)
# 加入多个
session.add_all([
User(name='wendy', fullname='Wendy Williams', nickname='windy'),
User(name='mary', fullname='Mary Contrary', nickname='mary'),
User(name='fred', fullname='Fred Flintstone', nickname='freddy')])
# 修改一个
ed_user.nickname = 'eddie'
# 确认操作
session.commit()