为什么这个SQLAlchemy示例提交了对DB的更改?

FMc*_*FMc 4 python session sqlalchemy commit

这个例子说明了我在构建的应用程序中遇到的一个谜.应用程序需要支持一个允许用户在不实际向DB提交更改的情况下执行代码的选项.但是,当我添加此选项时,我发现即使我没有调用该commit()方法,更改仍保留在DB中.

我的具体问题可以在代码注释中找到.基本目标是更清楚地了解SQLAlchemy何时以及为何将提交给DB.

我更广泛的问题是我的应用程序是应该(a)使用全局Session实例,还是(b)使用全局Session类,从中实例化特定实例.基于这个例子,我开始认为正确的答案是(b).是对的吗?编辑:此SQLAlchemy文档建议(b)建议.

import sys

from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class User(Base):
    __tablename__ = 'users'

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

    def __init__(self, name, age = 0):
        self.name = name
        self.age  = 0

    def __repr__(self):
        return "<User(name='{0}', age={1})>".format(self.name, self.age)

engine = create_engine('sqlite://', echo = False)
Base.metadata.create_all(engine)

Session = sessionmaker()
Session.configure(bind=engine)

global_session = Session() # A global Session instance.
commit_ages    = False     # Whether to commit in modify_ages().
use_global     = True      # If True, modify_ages() will commit, regardless
                           # of the value of commit_ages. Why?

def get_session():
    return global_session if use_global else Session()

def add_users(names):
    s = get_session()
    s.add_all(User(nm) for nm in names)
    s.commit()

def list_users():
    s = get_session()
    for u in s.query(User): print ' ', u

def modify_ages():
    s = get_session()
    n = 0
    for u in s.query(User):
        n += 10
        u.age = n
    if commit_ages: s.commit()

add_users(('A', 'B', 'C'))
print '\nBefore:'
list_users()
modify_ages()
print '\nAfter:'
list_users()
Run Code Online (Sandbox Code Playgroud)

sna*_*hoe 5

TL;医生-更新实际提交给他们正在进行中未提交的事务的一部分的数据库- .


我对create_engine()的调用进行了两次单独的更改.(除了这一行之外,我完全按照发布的方式使用您的代码.)

第一个是

engine = create_engine('sqlite://', echo = True)
Run Code Online (Sandbox Code Playgroud)

这提供了一些有用的信息.我不打算在这里发表的整个输出,但请注意没有SQL更新命令才会发出第二次调用list_users()是由:

...
After:
xxxx-xx-xx xx:xx:xx,xxx INFO sqlalchemy.engine.base.Engine.0x...d3d0 UPDATE users SET age=? WHERE users.id = ?
xxxx-xx-xx xx:xx:xx,xxx INFO sqlalchemy.engine.base.Engine.0x...d3d0 (10, 1)
...
Run Code Online (Sandbox Code Playgroud)

这是一个线索,数据不会持久存在,而是保留在会话对象中.

我做的第二个更改是将数据库保存到文件中

engine = create_engine('sqlite:///db.sqlite', echo = True)
Run Code Online (Sandbox Code Playgroud)

再次运行脚本为第二次调用list_users()提供了与之前相同的输出:

<User(name='A', age=10)>
<User(name='B', age=20)>
<User(name='C', age=30)>
Run Code Online (Sandbox Code Playgroud)

但是,如果您现在打开我们刚刚创建的数据库并查询其内容,您可以看到添加的用户被持久化到数据库,但年龄修改不是:

$ sqlite3 db.sqlite "select * from users"
1|A|0
2|B|0
3|C|0
Run Code Online (Sandbox Code Playgroud)

因此,对list_users()的第二次调用是从会话对象而不是从数据库获取其值,因为还有一个尚未提交的事务正在进行中.要证明这一点,请在脚本末尾添加以下行:

s = get_session()
s.rollback()
print '\nAfter rollback:'
list_users()
Run Code Online (Sandbox Code Playgroud)